From 2fd787fe3660272ad555e930bb43ceb9b7ec558c Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 6 Oct 2016 13:46:51 +0900 Subject: [PATCH] Imported Upstream version 1.5.0 Change-Id: I5683db92e912e2db33dddf08e5eba677b5d0c2d0 Signed-off-by: DongHun Kwak --- .gitignore | 2 + AUTHORS | 16 + HACKING | 56 - HACKING.md | 68 ++ Makefile.am | 2 +- NEWS | 141 +++ acinclude.m4 | 2 +- configure.ac | 6 +- doc/naturaldocs/conf/c_api/Languages.txt | 2 +- doc/naturaldocs/conf/c_api/Menu.txt | 2 +- doc/naturaldocs/conf/c_api/Topics.txt | 2 +- doc/naturaldocs/conf/lenses/Languages.txt | 4 +- doc/naturaldocs/conf/lenses/Menu.txt | 46 +- doc/naturaldocs/conf/lenses/Topics.txt | 2 +- lenses/aptconf.aug | 4 +- lenses/aptsources.aug | 34 +- lenses/chrony.aug | 171 ++- lenses/cron.aug | 1 + lenses/csv.aug | 51 + lenses/dhclient.aug | 3 +- lenses/erlang.aug | 12 + lenses/fstab.aug | 4 +- lenses/group.aug | 7 +- lenses/host_conf.aug | 2 +- lenses/httpd.aug | 45 +- lenses/inputrc.aug | 4 +- lenses/interfaces.aug | 10 +- lenses/json.aug | 51 +- lenses/keepalived.aug | 33 +- lenses/known_hosts.aug | 35 +- lenses/logrotate.aug | 1 + lenses/masterpasswd.aug | 148 +++ lenses/multipath.aug | 16 +- lenses/mysql.aug | 1 + lenses/nginx.aug | 19 +- lenses/ntp.aug | 7 +- lenses/openshift_quickstarts.aug | 5 +- lenses/openvpn.aug | 595 +++++++++-- lenses/pg_hba.aug | 1 + lenses/postgresql.aug | 1 + lenses/puppetfile.aug | 8 +- lenses/rabbitmq.aug | 23 +- lenses/reprepro_uploaders.aug | 39 +- lenses/rhsm.aug | 42 + lenses/rsyslog.aug | 6 +- lenses/rx.aug | 2 +- lenses/shellvars.aug | 142 ++- lenses/simplelines.aug | 1 + lenses/smbusers.aug | 2 +- lenses/spacevars.aug | 4 +- lenses/ssh.aug | 14 +- lenses/star.aug | 31 + lenses/sudoers.aug | 46 +- lenses/syslog.aug | 9 +- lenses/tests/test_aptconf.aug | 7 + .../{test_aptsource.aug => test_aptsources.aug} | 34 +- lenses/tests/test_chrony.aug | 98 +- lenses/tests/test_csv.aug | 91 ++ lenses/tests/test_cups.aug | 14 - lenses/tests/test_dhclient.aug | 3 + lenses/tests/test_erlang.aug | 8 + lenses/tests/test_group.aug | 5 + lenses/tests/test_host_conf.aug | 2 +- lenses/tests/test_httpd.aug | 125 ++- lenses/tests/test_inputrc.aug | 7 + lenses/tests/test_interfaces.aug | 5 + lenses/tests/test_json.aug | 168 ++- lenses/tests/test_keepalived.aug | 509 +++++---- lenses/tests/test_known_hosts.aug | 32 +- lenses/tests/test_masterpasswd.aug | 125 +++ lenses/tests/test_multipath.aug | 34 +- lenses/tests/test_nginx.aug | 65 ++ lenses/tests/test_ntp.aug | 12 +- lenses/tests/test_openshift_quickstarts.aug | 46 + lenses/tests/test_openvpn.aug | 1114 +++++++++++++++++++- lenses/tests/test_properties.aug | 4 +- lenses/tests/test_puppetfile.aug | 7 + lenses/tests/test_rabbitmq.aug | 20 + lenses/tests/test_reprepro_uploaders.aug | 42 + lenses/tests/test_rhsm.aug | 151 +++ lenses/tests/test_rsyslog.aug | 18 + lenses/tests/test_shellvars.aug | 237 ++++- lenses/tests/test_smbusers.aug | 2 + lenses/tests/test_spacevars.aug | 4 +- lenses/tests/test_ssh.aug | 28 + lenses/tests/test_star.aug | 63 ++ lenses/tests/test_sudoers.aug | 31 +- lenses/tests/test_syslog.aug | 6 + lenses/tests/test_tmpfiles.aug | 359 +++++++ lenses/tests/test_trapperkeeper.aug | 141 +++ lenses/tests/test_xml.aug | 7 +- lenses/tests/test_yaml.aug | 75 ++ lenses/tmpfiles.aug | 106 ++ lenses/trapperkeeper.aug | 123 +++ lenses/util.aug | 15 +- lenses/vsftpd.aug | 2 +- lenses/xml.aug | 2 +- lenses/yaml.aug | 74 ++ man/augparse.pod | 11 + man/augtool.1 | 16 +- man/augtool.pod | 11 +- src/ast.c | 2 +- src/augeas.c | 39 +- src/augeas.h | 2 +- src/augparse.c | 4 +- src/augrun.c | 5 +- src/augtool.c | 73 +- src/builtin.c | 2 +- src/errcode.c | 2 +- src/errcode.h | 2 +- src/fa.c | 25 +- src/fa.h | 25 +- src/get.c | 2 +- src/info.c | 2 +- src/info.h | 2 +- src/internal.c | 2 +- src/internal.h | 5 +- src/jmt.c | 2 +- src/jmt.h | 2 +- src/lens.c | 26 +- src/lens.h | 8 +- src/list.h | 2 +- src/memory.c | 2 +- src/memory.h | 2 +- src/pathx.c | 122 ++- src/put.c | 12 +- src/ref.c | 2 +- src/ref.h | 2 +- src/regexp.c | 2 +- src/regexp.h | 2 +- src/syntax.c | 2 +- src/syntax.h | 2 +- src/transform.c | 2 +- src/transform.h | 2 +- src/try | 2 + tests/Makefile.am | 14 +- tests/fatest.c | 29 +- tests/lens-test-1 | 1 + tests/modules/pass_format_atype.aug | 12 + .../ifcfg-Auto-ALICE-WLAN38_(automatisch) | 0 .../ifcfg-Auto_FRITZ!Box_Fon_WLAN_7112 | 14 - tests/test-api.c | 24 +- tests/test-interpreter.sh | 2 +- tests/test-load.c | 2 +- tests/test-perf.c | 111 ++ tests/test-run.c | 2 +- tests/test-save.c | 7 +- tests/test-xpath.c | 2 +- tests/xpath.tests | 6 +- 149 files changed, 5859 insertions(+), 797 deletions(-) delete mode 100644 HACKING create mode 100644 HACKING.md create mode 100644 lenses/csv.aug create mode 100644 lenses/masterpasswd.aug create mode 100644 lenses/rhsm.aug create mode 100644 lenses/star.aug rename lenses/tests/{test_aptsource.aug => test_aptsources.aug} (66%) create mode 100644 lenses/tests/test_csv.aug create mode 100644 lenses/tests/test_masterpasswd.aug create mode 100644 lenses/tests/test_rhsm.aug create mode 100644 lenses/tests/test_star.aug create mode 100644 lenses/tests/test_tmpfiles.aug create mode 100644 lenses/tests/test_trapperkeeper.aug create mode 100644 lenses/tests/test_yaml.aug create mode 100644 lenses/tmpfiles.aug create mode 100644 lenses/trapperkeeper.aug create mode 100644 lenses/yaml.aug create mode 100644 tests/modules/pass_format_atype.aug delete mode 100644 tests/root/etc/sysconfig/network-scripts/ifcfg-Auto-ALICE-WLAN38_(automatisch) delete mode 100644 tests/root/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ!Box_Fon_WLAN_7112 create mode 100644 tests/test-perf.c diff --git a/.gitignore b/.gitignore index 72adb8d..1693fe3 100644 --- a/.gitignore +++ b/.gitignore @@ -35,12 +35,14 @@ src/parser.[ch] src/lexer.[ch] src/augtool src/augparse +src/callgrind.* tests/fatest tests/leak tests/lens-*.sh tests/test-api tests/test-xpath tests/test-load +tests/test-perf tests/test-run tests/test-save tests/*.trs diff --git a/AUTHORS b/AUTHORS index ed24a3c..99d6174 100644 --- a/AUTHORS +++ b/AUTHORS @@ -49,6 +49,7 @@ Contributions by: Andrew Colin Kissa Francois Lebel Frédéric Lespez + Miroslav Lichvar Jasper Lievisse Adriaanse Tom Limoncelli Erinn Looney-Triggs @@ -123,3 +124,18 @@ Contributions by: Frank Grötzner Pino Toscano Geoffrey Gardella + Matt Dainty + Jan Doleschal + Joe Topjian + Julien Pivotto + Gregory Smith + Justin Akers + Oliver Mangold + Geoff Williams + Florian Chazal + Dimitar Dimitrov + Cédric Bosdonnat + Christoph Maser + Chris Reeves + Gerlof Fokkema + Daniel Trebbien diff --git a/HACKING b/HACKING deleted file mode 100644 index 7231a06..0000000 --- a/HACKING +++ /dev/null @@ -1,56 +0,0 @@ -This file explains some details about developing the Augeas C library. - -Check out the sources ---------------------- - -The sources are in a git repo (which you presumably found already) - - git clone git://github.com/hercules-team/augeas - -Building from git ------------------ - - Besides the usual build tools (gcc, autoconf, automake etc.) you need the - following tools and libraries to build Augeas: - - * Bison - * Flex - * readline-devel - * libxml2-devel - * libselinux-devel (optional) - - Augeas uses gnulib, and you need a checkout of gnulib. The build scripts - can create a checkout for you behind the scenes - though if you already - have a gnulib checkout, you can pass its location to autogen.sh with the - --gnulib-srcdir option. - - At its simplest, you build Augeas from git by running the following - commands in the toplevel directory of your Augeas checkout: - - ./autogen.sh [--gnulib-srcdir=$GNULIB_CHECKOUT] - make && make install - - It is recommended though to turn on a few development features when - building; in particular, stricter compiler warnings and some debug - logging. You can pass these options either to autogen.sh or to - configure. You'd then run autogen like this: - - ./autogen.sh --enable-compile-warnings=error --enable-debug=yes - -Running augtool ---------------- - -The script ./src/try can be used to run ./src/augtool against a fresh -filesystem root. It copies the files from tests/root/ to build/try/ and -starts augtool against that root, using the lenses from lenses/ (and none -of the ones that might be installed on your system) - -The script can be used for the following; OPTS are options that are passed -to augtool verbatim - - ./src/try OPTS - run the commands from build/augcmds.txt - ./src/try cli OPTS - start an interactive session with augtool - ./src/try gdb - start gdb and set it up from build/gdbcmds.txt for - debugging augtool with commands in build/augcmds.txt - ./src/try valgrind - run the commands from build/augcmds.txt through augtool - under valgrind to check for memory leaks diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..efd993d --- /dev/null +++ b/HACKING.md @@ -0,0 +1,68 @@ +This file explains some details about developing the Augeas C library. + +# Check out the sources + +The sources are in a git repo; to check it out run + +``` + git clone git://github.com/hercules-team/augeas +``` + +# Building from git + +Besides the usual build tools (gcc, autoconf, automake etc.) you need the +following tools and libraries to build Augeas: + +* Bison +* Flex +* readline-devel +* libxml2-devel +* libselinux-devel (optional) + +Augeas uses gnulib, and you need a checkout of gnulib. The build scripts +can create a checkout for you behind the scenes - though if you already +have a gnulib checkout, you can pass its location to autogen.sh with the +--gnulib-srcdir option. + +At its simplest, you build Augeas from git by running the following +commands in the toplevel directory of your Augeas checkout: + + ./autogen.sh [--gnulib-srcdir=$GNULIB_CHECKOUT] + make && make install + +It is recommended though to turn on a few development features when +building; in particular, stricter compiler warnings and some debug +logging. You can pass these options either to autogen.sh or to +configure. You'd then run autogen like this: + + ./autogen.sh --enable-compile-warnings=error --enable-debug=yes + +# Running augtool + +The script ./src/try can be used to run ./src/augtool against a fresh +filesystem root. It copies the files from tests/root/ to build/try/ and +starts augtool against that root, using the lenses from lenses/ (and none +of the ones that might be installed on your system) + +The script can be used for the following; OPTS are options that are passed +to augtool verbatim + +* `./src/try OPTS`: run the commands from `build/augcmds.txt` +* `./src/try cli OPTS`: start an interactive session with augtool +* `./src/try gdb`: start gdb and set it up from `build/gdbcmds.txt` for + debugging augtool with commands in `build/augcmds.txt` +* `./src/try valgrind`: run the commands from `build/augcmds.txt` through + augtool under valgrind to check for memory leaks + +# Platform specific notes + +## Mac OSX + +OSX comes with a crippled reimplementation of readline, `libedit`; while +Augeas will build against `libedit`, you can get full readline +functionality by installing the `readline` package from +[Homebrew](http://brew.sh/) and setting the following: +```sh +> export CPPFLAGS=-I/usr/local/opt/readline/include +> export LDFLAGS=-L/usr/local/opt/readline/lib +``` diff --git a/Makefile.am b/Makefile.am index d88e6dd..87d511a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,7 @@ lenstestdir=$(datadir)/augeas/lenses/dist/tests dist_lens_DATA=$(wildcard lenses/*.aug) dist_lenstest_DATA=$(wildcard lenses/tests/*.aug) -EXTRA_DIST=augeas.spec build/ac-aux/move-if-change Makefile.am HACKING +EXTRA_DIST=augeas.spec build/ac-aux/move-if-change Makefile.am HACKING.md pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = augeas.pc diff --git a/NEWS b/NEWS index a748343..7fdf6cd 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,144 @@ +1.5.0 - 2015-05-11 + - General changes/additions + * augtool: new --timing option that prints after each operation how long + it took + * augtool: print brief help message when incorrect options are given rather + than dumping all help text + * Path expressions: optimize performance of evaluating certain + expressions + * lots of safety improvements in libfa to avoid using uninitialized + values and the like (Daniel Trebbien) + * tolerate building against OSX' libedit (Issue #256) + - API changes + * aug_match: fix a bug where expressions like /foo/*[2] would match a + hidden node and pretend there was no match at all. We now make sure + we never match a hidden node. Thanks to Xavier Mol for reporting the + problem. + * aug_get: make sure we set *value to NULL, even if the provided path is + invalid (Issue #372) + * aug_rm: fix segfault when deleting a tree and one of its ancestors + (Issue #319) + * aug_save: fix segfault when trying to save an invalid subtree. A + routine that was generating details for the error message overflowed + a buffer it had created (Issue #349) + - Lens changes/additions + * AptConf: support hash comments + * AptSources: support options (Issue #295), + support brackets with spaces in URI (GH #296) + rename test file to test_aptsources.aug + * Chrony: allow signed numbers and indentation, fix stray EOL entry, + disallow comment on EOL, add many missing directives and + options (Miroslav Lichvar, RHBZ#1213281) + add new directives and options that were added in + chrony-2.2 and chrony-2.3 and improve parsing of + access configuration (Miroslav Lichvar, Issue #348) + add new options for chrony-2.4 (Miroslav Lichvar) + * Dhclient: avoid put ambiguity for node without value (Issue #294) + * Group: support NIS map, support an overridden and disabled password, + i.e. `+:*::` (Matt Dainty) (Issue #258) + * Host_Conf: support spaces between list items (Cedric Bosdonnat, Issue #358) + * Httpd: add paths to SLES vhosts + (Jan Doleschal) (Issue #268) + parse backslashes in directive arguments (Issue #307) + parse mismatching case of opening/closing tags + parse multiple ending section tags on one line + parse wordlists in braces in SSLRequire directives + parse directive args starting with double quote (Issue #330) + parse directive args containing quotes + support perl directives (Issue #327) + parse line breaks/continuations in section arguments + parse escaped spaces in directive/section arguments + parse backslashes at the start of directive args (Issue #324) + * Inputrc: support $else (Cedric Bosdonnat, Issue #359) + * Interfaces: add support for source-directory (Issue #306) + * Json: add comments support, refactor, + allow escaped quotes and blackslashes + * Keepalived: fix space/tag alignments and hanging spaces, + add vrrp_mcast_group4 and vrrp_mcast_group6, + add more vrrp_instance flags, + add mcast/unicast_src_ip and unicast_peer, + add missing garp options, + add vrrp_script options, + expand vrrp_sync_group block, + allow notify option + (Joe Topjian) (Issue #266) + * Known_Hosts: refactoring and description fixed + * Logrotate: support dateyesterday option (Chris Reeves) (GH #367, #368) + * MasterPasswd: new lens to parse /etc/master.passwd + (Matt Dainty) (Issue #258) + * Multipath: add various missing keywoards (Olivier Mangold) (Issue #289) + * MySQL: include /etc/my.cnf.d/*.cnf (Issue #353) + * Nginx: improve typechecking of lens, + allow masks in IP keys and IPv6 (Issue #260) + add @server simple nodes (Issue #335) + * Ntp: add support for basic interface syntax + * OpenShift_Quickstarts: Use Json.lns + * OpenVPN: add all options available in OpenVPN 2.3o + (Justin Akers) (Issue #278) + * Puppetfile: name separator is not mandatory + add support for moduledir (Christoph Maser) + * Rabbitmq: remove space in option name, + add support for cluster_partitioning_handling, + add missing simple options (Joe Topjian) (Issue #264) + * Reprepro_Uploaders: add support for distribution field + (Mathieu Alorent) (Issue #277), + add support for groups (Issue #283) + * Rhsm: new lens to parse subscription-manager's /etc/rhsm/rhsm.conf + * Rsyslog: improve property filter parsing, + treat whitespace after commas as optional. + recognize '~' as a valid syslog action (discard) + (Gregory Smith) (Issue #282), + add support for redirecting output to named pipes + (Gerlof Fokkema) (Issue #366) + * Shellvars: allow partial quoting, mixing multiple styles + (Kaarle Ritvanen) (Issue #183); + allow wrapping builtin argument to multiple lines + (Kaarle Ritvanen) (Issue #184); + support ;; on same line with multiple commands + (Kaarle Ritvanen) (Issue #185); + allow line wrapping and improve quoting support + (Kaarle Ritvanen) (Issue #187); + accept [] and [[]] builtins (Issue #188); + allow && and || constructs after condition + (Kaarle Ritvanen) (Issue #265); + add pattern nodes in case entries + (BREAKING CHANGE: case entry values are now in a + @pattern subnode) (Kaarle Ritvanen) (Issue #265) + add eval builtin support; + add alias builtin support; + allow (almost) any command; + allow && and || after commands (Issue #215); + allow wrapping command sequences + (Kaarle Ritvanen) (Issue #333); + allow command-specific environment variable + (Kaarle Ritvanen) (Issue #332); + support subshells (Issue #339) + newlines in start of functions + allow newlines after actions + support comments after function name (Issue #339) + exclude SuSEfirewall2 (Cedric Bosdonnat, Issue #357) + * Simplelines: parse OpenBSD's hostname.if(5) + files (Jasper Lievisse Adriaanse) (Issue #252) + * Smbusers: add support for ; comments + * Spacevars: support flags (Issue #279) + * Ssh: add support for HostKeyAlgorithms, KexAlgorithms + and PubkeyAcceptedKeyTypes (Oliver Mangold) (Issue #290), + add support for GlobalKnownHostsFile (Issue #316) + * Star: New lens to parse /etc/default/star + * Sudoers: support for negated command alias + (Geoff Williams) (Issue #262) + * Syslog: recognize '~' as a valid syslog action (discard) + (Gregory Smith) (Issue #282) + * Tmpfiles: new lens to parse systemd's tempfiles.d configuration + files (Julien Pivotto) (Issue #269) + * Trapperkeeper: new lens for Puppet server configuration files + * Util: add comment_c_style_or_hash lens + add empty_any lens + * Vsftpd: add isolate and isolate_network options + (Florian Chazal) (Issue #334) + * Xml: allow empty document (Issue #255) + * YAML: new lens (subset) (Dimitar Dimitrov) (Issue #338) + 1.4.0 - 2015-05-22 - General changes/additions * add a aug_escape_name call to sanitize strings for use in path diff --git a/acinclude.m4 b/acinclude.m4 index f632182..31988b1 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -142,7 +142,7 @@ AC_DEFUN([AUGEAS_CHECK_READLINE], [ if test $use_readline = yes; then saved_libs=$LIBS LIBS=$READLINE_LIBS - AC_CHECK_FUNCS([rl_completion_matches]) + AC_CHECK_FUNCS([rl_completion_matches rl_crlf rl_replace_line]) LIBS=$saved_libs fi ]) diff --git a/configure.ac b/configure.ac index 5654723..72b6984 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(augeas, 1.4.0) +AC_INIT(augeas, 1.5.0) AC_CONFIG_SRCDIR([src/augeas.c]) AC_CONFIG_AUX_DIR([build/ac-aux]) AM_CONFIG_HEADER([config.h]) @@ -65,8 +65,8 @@ if test x"$enable_debug" = x"yes"; then fi dnl Version info in libtool's notation -AC_SUBST([LIBAUGEAS_VERSION_INFO], [20:0:20]) -AC_SUBST([LIBFA_VERSION_INFO], [5:1:4]) +AC_SUBST([LIBAUGEAS_VERSION_INFO], [20:1:20]) +AC_SUBST([LIBFA_VERSION_INFO], [5:2:4]) AC_GNU_SOURCE diff --git a/doc/naturaldocs/conf/c_api/Languages.txt b/doc/naturaldocs/conf/c_api/Languages.txt index 85d5fde..42b197c 100644 --- a/doc/naturaldocs/conf/c_api/Languages.txt +++ b/doc/naturaldocs/conf/c_api/Languages.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 # This is the Natural Docs languages file for this project. If you change # anything here, it will apply to THIS PROJECT ONLY. If you'd like to change diff --git a/doc/naturaldocs/conf/c_api/Menu.txt b/doc/naturaldocs/conf/c_api/Menu.txt index f56a34d..c8fdf1b 100644 --- a/doc/naturaldocs/conf/c_api/Menu.txt +++ b/doc/naturaldocs/conf/c_api/Menu.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 Title: Augeas Documentation diff --git a/doc/naturaldocs/conf/c_api/Topics.txt b/doc/naturaldocs/conf/c_api/Topics.txt index 2153090..905270f 100644 --- a/doc/naturaldocs/conf/c_api/Topics.txt +++ b/doc/naturaldocs/conf/c_api/Topics.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 # This is the Natural Docs topics file for this project. If you change anything # here, it will apply to THIS PROJECT ONLY. If you'd like to change something diff --git a/doc/naturaldocs/conf/lenses/Languages.txt b/doc/naturaldocs/conf/lenses/Languages.txt index 76a4e02..499f12a 100644 --- a/doc/naturaldocs/conf/lenses/Languages.txt +++ b/doc/naturaldocs/conf/lenses/Languages.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 # This is the Natural Docs languages file for this project. If you change # anything here, it will apply to THIS PROJECT ONLY. If you'd like to change @@ -118,6 +118,6 @@ Language: Augeas Extension: aug Block Comment: (* *) Augeas Variable Prototype Enders: \n let test module filter - Augeas Lens Prototype Enders: \n let test module filter Augeas Test Prototype Enders: \n let test module filter + Augeas Lens Prototype Enders: \n let test module filter Perl Package: NaturalDocs::Languages::Augeas diff --git a/doc/naturaldocs/conf/lenses/Menu.txt b/doc/naturaldocs/conf/lenses/Menu.txt index c245446..78820a2 100644 --- a/doc/naturaldocs/conf/lenses/Menu.txt +++ b/doc/naturaldocs/conf/lenses/Menu.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 Title: Augeas Documentation @@ -24,6 +24,11 @@ SubTitle: Modules # yyyy - Four digit year. 2006 is "2006" # year - Four digit year. 2006 is "2006" +# These are indexes you deleted, so Natural Docs will not add them again +# unless you remove them from this line. + +Don't Index: Properties + # -------------------------------------------------------------------------- # @@ -81,6 +86,7 @@ Group: Specific Modules { File: CPanel (cpanel.aug) File: Cron (cron.aug) File: Crypttab (crypttab.aug) + File: CSV (csv.aug) File: Cups (cups.aug) File: Debctrl (no auto-title, debctrl.aug) File: Desktop (desktop.aug) @@ -133,7 +139,7 @@ Group: Specific Modules { File: OpenShift_Config (openshift_config.aug) File: OpenShift_Http (openshift_http.aug) File: OpenShift_Quickstarts (openshift_quickstarts.aug) - File: Pagekite (pagekite.aug) + File: Pagekite (pagekite.aug) File: Pam (pam.aug) File: PamConf (pamconf.aug) File: Passwd (passwd.aug) @@ -151,6 +157,7 @@ Group: Specific Modules { File: Redis (redis.aug) File: Reprepro_Uploaders (reprepro_uploaders.aug) File: Resolv (resolv.aug) + File: Rhsm (rhsm.aug) File: Rmt (rmt.aug) File: Rsyslog (rsyslog.aug) File: Schroot (schroot.aug) @@ -167,6 +174,7 @@ Group: Specific Modules { File: Ssh (ssh.aug) File: Sshd (sshd.aug) File: Sssd (no auto-title, sssd.aug) + File: Star (star.aug) File: Subversion (subversion.aug) File: Sudoers (sudoers.aug) File: Sysconfig_Route (sysconfig_route.aug) @@ -174,15 +182,24 @@ Group: Specific Modules { File: Syslog (syslog.aug) File: Systemd (systemd.aug) File: Thttpd (thttpd.aug) + File: Tmpfiles (tmpfiles.aug) File: Tuned (tuned.aug) File: Up2date (up2date.aug) File: UpdateDB (updatedb.aug) File: VWware_Config (vmware_config.aug) File: Vfstab (vfstab.aug) File: Xinetd (xinetd.aug) - File: Xymon (xymon.aug) - File: XymonAlerting (xymonalerting.aug) File: Xorg (xorg.aug) + File: ClamAV (clamav.aug) + File: Dns_Zone (dns_zone.aug) + File: Mailscanner (mailscanner.aug) + File: Mailscanner_rules (mailscanner_rules.aug) + File: MasterPasswd (masterpasswd.aug) + File: Pgbouncer (pgbouncer.aug) + File: PylonsPaste (pylonspaste.aug) + File: Trapperkeeper (trapperkeeper.aug) + File: Xymon_Alerting (xymon_alerting.aug) + File: Yaml (yaml.aug) } # Group: Specific Modules Group: Generic Modules { @@ -212,9 +229,9 @@ Group: Tests and Examples { File: Test_Carbon (tests/test_carbon.aug) File: Test_Channels (tests/test_channels.aug) File: Test_Chrony (tests/test_chrony.aug) - File: Test_ClamAV (tests/test_clamav.aug) File: Test_Collectd (tests/test_collectd.aug) File: Test_CPanel (tests/test_cpanel.aug) + File: Test_CSV (no auto-title, tests/test_csv.aug) File: Test_Cups (tests/test_cups.aug) File: Test_Dovecot (tests/test_dovecot.aug) File: Test_Erlang (tests/test_erlang.aug) @@ -236,8 +253,6 @@ Group: Tests and Examples { File: Test_Ldso (tests/test_ldso.aug) File: Test_Lightdm (tests/test_lightdm.aug) File: Test_LVM (tests/test_lvm.aug) - File: Test_Mailscanner (tests/test_mailscanner.aug) - File: Test_Mailscanner_Rules (tests/test_mailscanner_rules.aug) File: Test_MCollective (tests/test_mcollective.aug) File: Test_Memcached (tests/test_memcached.aug) File: Test_MongoDBServer (tests/test_mongodbserver.aug) @@ -248,20 +263,18 @@ Group: Tests and Examples { File: Test_OpenShift_Config (tests/test_openshift_config.aug) File: Test_OpenShift_Http (tests/test_openshift_http.aug) File: Test_OpenShift_Quickstarts (tests/test_openshift_quickstarts.aug) - File: Test_Pgbouncer (tests/test_pgbouncer.aug) File: Test_Postfix_Transport (tests/test_postfix_transport.aug) File: Test_Postfix_Virtual (tests/test_postfix_virtual.aug) File: Test_Postgresql (tests/test_postgresql.aug) File: Test_Protocols (tests/test_protocols.aug) File: Test_Puppet_Auth (tests/test_puppet_auth.aug) File: Test_Puppetfile (tests/test_puppetfile.aug) - File: Test_Pylonspaste (tests/test_pylonspaste.aug) File: Test_Qpid (tests/test_qpid.aug) File: Test_Quote (tests/test_quote.aug) File: Test_Rabbitmq (tests/test_rabbitmq.aug) File: Test_Redis (tests/test_redis.aug) File: Test_Reprepro_Uploaders (tests/test_reprepro_uploaders.aug) - File: Test_Rmt (tests/test_rmt.aug) + File: Test_Rhsm (tests/test_rhsm.aug) File: Test_Rsyslog (tests/test_rsyslog.aug) File: Test_Simplelines (tests/test_simplelines.aug) File: Test_Simplevars (tests/test_simplevars.aug) @@ -271,19 +284,26 @@ Group: Tests and Examples { File: Test_Sysctl (tests/test_sysctl.aug) File: Test_Systemd (tests/test_systemd.aug) File: Test_Thttpd (tests/test_thttpd.aug) - File: Test_Tuned (tests/test_tuned.aug) + File: Test_Tmpfiles (tests/test_tmpfiles.aug) File: Test_Up2date (tests/test_up2date.aug) File: Test_UpdateDB (tests/test_updatedb.aug) File: Test_VMware_Config (tests/test_vmware_config.aug) File: Test_Xml (tests/test_xml.aug) - File: Test_Xymon (tests/test_xymon.aug) - File: Test_XymonAlerting (tests/test_xymonalerting.aug) File: Test_Yum (tests/test_yum.aug) File: Test_login_defs (tests/test_login_defs.aug) File: Test_sssd (tests/test_sssd.aug) File: Test_sudoers (no auto-title, tests/test_sudoers.aug) File: Test_ssh (tests/test_ssh.aug) File: Test_sshd (tests/test_sshd.aug) + File: AptPreferences.pin (tests/test_aptpreferences.aug) + File: Desktop.lns (tests/test_desktop.aug) + File: Interfaces.lns (tests/test_interfaces.aug) + File: test_httpd.aug (tests/test_httpd.aug) + File: test_shellvars.aug (tests/test_shellvars.aug) + File: test_shellvars_list.aug (tests/test_shellvars_list.aug) + File: test_slapd.aug (tests/test_slapd.aug) + File: test_trapperkeeper.aug (tests/test_trapperkeeper.aug) + File: Test_Xymon_Alerting (tests/test_xymon_alerting.aug) } # Group: Tests and Examples Group: Index { diff --git a/doc/naturaldocs/conf/lenses/Topics.txt b/doc/naturaldocs/conf/lenses/Topics.txt index 3bbb218..7054ff0 100644 --- a/doc/naturaldocs/conf/lenses/Topics.txt +++ b/doc/naturaldocs/conf/lenses/Topics.txt @@ -1,4 +1,4 @@ -Format: 1.51 +Format: 1.52 # This is the Natural Docs topics file for this project. If you change anything # here, it will apply to THIS PROJECT ONLY. If you'd like to change something diff --git a/lenses/aptconf.aug b/lenses/aptconf.aug index 8683c7a..99d5730 100644 --- a/lenses/aptconf.aug +++ b/lenses/aptconf.aug @@ -33,7 +33,7 @@ let eol = Util.eol (* View: empty A C-style empty line *) -let empty = Util.empty_c_style +let empty = Util.empty_any (* View: indent An indentation *) @@ -41,7 +41,7 @@ let indent = Util.indent (* View: comment_simple A one-line comment, C-style *) -let comment_simple = Util.comment_c_style +let comment_simple = Util.comment_c_style_or_hash (* View: comment_multi A multiline comment, C-style *) diff --git a/lenses/aptsources.aug b/lenses/aptsources.aug index 10c8325..d7a6b3b 100644 --- a/lenses/aptsources.aug +++ b/lenses/aptsources.aug @@ -10,7 +10,7 @@ module Aptsources = * Group: Utility variables/functions ************************************************************************) (* View: sep_ws *) - let sep_ws = del /[ \t]+/ " " + let sep_ws = Sep.space (* View: eol *) let eol = Util.del_str "\n" @@ -21,18 +21,36 @@ module Aptsources = let empty = Util.empty (* View: word *) - let word = /[^# \n\t]+/ + let word = /[^][# \n\t]+/ + + (* View: uri *) + let uri = + let protocol = /[a-z+]+:/ + in let path = /\/[^] \t]*/ + in let path_brack = /\[[^]]+\]\/?/ + in protocol? . path + | protocol . path_brack (************************************************************************ * Group: Keywords ************************************************************************) (* View: record *) - let record = [ Util.indent . seq "source" . [ label "type" . store word ] . sep_ws . - [ label "uri" . store word ] . sep_ws . - [ label "distribution" . store word ] . - [ label "component" . sep_ws . store word ]* . - del /[ \t]*(#.*)?/ "" - . eol ] + let record = + let option_sep = [ label "operation" . store /[+-]/]? . Sep.equal + in let option = Build.key_value /arch|trusted/ option_sep (store Rx.word) + in let options = [ label "options" + . Util.del_str "[" . Sep.opt_space + . Build.opt_list option Sep.space + . Sep.opt_space . Util.del_str "]" + . sep_ws ] + in [ Util.indent . seq "source" + . [ label "type" . store word ] . sep_ws + . options? + . [ label "uri" . store uri ] . sep_ws + . [ label "distribution" . store word ] + . [ label "component" . sep_ws . store word ]* + . del /[ \t]*(#.*)?/ "" + . eol ] (************************************************************************ * Group: Lens diff --git a/lenses/chrony.aug b/lenses/chrony.aug index 3257568..783fc9f 100644 --- a/lenses/chrony.aug +++ b/lenses/chrony.aug @@ -9,13 +9,6 @@ About: Reference See http://chrony.tuxfamily.org/manual.html#Configuration-file -About: Limitations - Does not (currently) support - - include - - manual - - refclock - - tempcomp - About: License This file is licenced under the LGPL v2+, like the rest of Augeas. @@ -50,14 +43,17 @@ module Chrony = let word = Rx.word (* Variable: integer *) - let integer = Rx.integer + let integer = Rx.relinteger (* Variable: decimal *) - let decimal = Rx.decimal + let decimal = Rx.reldecimal (* Variable: ip *) let ip = Rx.ip + (* Variable: path *) + let path = Rx.fspath + (************************************************************************ * Group: Create required expressions ************************************************************************) @@ -80,24 +76,60 @@ module Chrony = let no_space = /[^ \t\r\n!;#%]+/ (* Variable: cmd_options - Server/Peer options with values + Server/Peer/Pool options with values *) let cmd_options = "key" | /maxdelay((dev)?ratio)?/ | /(min|max)poll/ + | /(min|max)samples/ + | "maxsources" | "polltarget" | "port" | "presend" + | "version" (* Variable: cmd_flags - Server/Peer options without values + Server/Peer/Pool options without values *) let cmd_flags = "auto_offline"|"iburst"|"noselect"|"offline"|"prefer" + |"require"|"trust" + + (* Variable: ntp_source + Server/Peer/Pool key names + *) + let ntp_source = "server"|"peer"|"pool" + + (* Variable: allowdeny_types + Key names for access configuration + *) + let allowdeny_types = "allow"|"deny"|"cmdallow"|"cmddeny" + + (* Variable: local_options + local options with values + *) + let local_options = "stratum"|"distance" + + (* Variable: local_flags + local options without values + *) + let local_flags = "orphan" - (* Variable: server_peer - Server/Peer key names + (* Variable: ratelimit_options + Rate limiting options with values *) - let server_peer = "server"|"peer" + let ratelimit_options = "interval"|"burst"|"leak" + + (* Variable: refclock_options + refclock options with values + *) + let refclock_options = "refid"|"lock"|"poll"|"dpoll"|"filter"|"rate" + |"minsamples"|"maxsamples"|"offset"|"delay" + |"precision"|"maxdispersion" + + (* Variable: refclock_flags + refclock options without values + *) + let refclock_flags = "noselect"|"prefer"|"require"|"trust" (* Variable: flags Options without values @@ -105,6 +137,7 @@ module Chrony = let flags = "dumponexit" | "generatecommandkey" | "lock_all" + | "manual" | "noclientlog" | "rtconutc" | "rtcsync" @@ -117,15 +150,17 @@ module Chrony = (* Variable: simple_keys Options with single values *) - let simple_keys = "acquisitionport" | "allow" | "bindaddress" - | "bindcmdaddress" | "cmdallow" | "cmddeny" - | "combinelimit" | "commandkey" | "cmdport" - | "corrtimeratio" | "deny" | "driftfile" - | "dumpdir" | "keyfile" | "leapsectz" | "linux_hz" - | "linux_freq_scale" | "logbanner" | "logchange" - | "logdir" | "maxclockerror" | "maxsamples" - | "maxupdateskew" | "minsamples" | "clientloglimit" - | "pidfile" | "port" | "reselectdist" | "rtcdevice" + let simple_keys = "acquisitionport" | "bindacqaddress" + | "bindaddress" | "bindcmdaddress" | "clientloglimit" + | "combinelimit" | "commandkey" + | "cmdport" | "corrtimeratio" | "driftfile" + | "dumpdir" | "hwclockfile" | "include" | "keyfile" + | "leapsecmode" | "leapsectz" | "linux_freq_scale" + | "linux_hz" | "logbanner" | "logchange" | "logdir" + | "maxdistance" | "maxdrift" + | "maxclockerror" | "maxsamples" | "maxslewrate" + | "maxupdateskew" | "minsamples" | "minsources" | "pidfile" + | "port" | "reselectdist" | "rtcautotrim" | "rtcdevice" | "rtcfile" | "sched_priority" | "stratumweight" | "user" (************************************************************************ @@ -144,37 +179,50 @@ module Chrony = * Group: Lenses for parsing out sections ************************************************************************) (* View: all_flags - match all flags using Build.flag_line + options without any arguments *) - let all_flags = Build.flag_line flags + let all_flags = [ Util.indent . key flags . eol ] (* View: kv options with only one arg can be directly mapped to key = value *) - let kv = Build.key_value_line_comment simple_keys space (store no_space) comment + let kv = [ Util.indent . key simple_keys . space . (store no_space) . eol ] (* Property: Options with multiple values Each of these gets their own parsing block - - server|peer
+ - server|peer|pool
+ - allow|deny|cmdallow|cmddeny [all] [] - log - broadcast
- fallbackdrift - initstepslew - - local stratum + - local - mailonchange - makestep - maxchange + - ratelimit|cmdratelimit + - refclock + - smoothtime + - tempcomp ( | ) *) (* View: host_list - Find all ntp servers/peers and their flags/options + Find all NTP sources and their flags/options *) - let host_list = [ Util.indent . key server_peer + let host_list = [ Util.indent . key ntp_source . space . store address_re . ( host_flags | host_options )* . eol ] + (* View: allowdeny + allow/deny/cmdallow/cmddeny has a specific syntax + *) + let allowdeny = [ Util.indent . key allowdeny_types + . [ space . key "all" ]? + . ( space . store ( no_space - "all" ) )? + . eol ] + (* View: log_list log has a specific options list *) @@ -186,7 +234,8 @@ module Chrony = let bcast = [ Util.indent . key "broadcast" . space . [ label "interval" . store integer ] . space . store_address - . ( space . [ label "port" . store integer] | eol) ] + . ( space . [ label "port" . store integer ] )? + . eol ] (* View: fdrift fallbackdrift has specific syntax @@ -207,9 +256,11 @@ module Chrony = (* View: local local has specific syntax *) - let local = [ Util.indent . key "local" . space - . [ key "stratum" . space . store integer ] - . eol ] + let local = [ Util.indent . key "local" + . ( space . ( [ key local_flags ] + | [ key local_options . space . store no_space ] ) + )* + . eol ] (* View: email mailonchange has specific syntax @@ -242,14 +293,62 @@ module Chrony = . [ label "limit" . store integer ] . eol ] + (* View: ratelimit + ratelimit/cmdratelimit has specific syntax + *) + let ratelimit = [ Util.indent . key /(cmd)?ratelimit/ + . [ space . key ratelimit_options + . space . store no_space ]* + . eol ] + (* View: refclock + refclock has specific syntax + *) + let refclock = [ Util.indent . key "refclock" + . space + . [ label "driver" . store word ] + . space + . [ label "parameter" . store no_space ] + . ( space . ( [ key refclock_flags ] + | [ key refclock_options . space . store no_space ] ) + )* + . eol ] + + (* View: smoothtime + smoothtime has specific syntax + *) + let smoothtime = [ Util.indent . key "smoothtime" + . space + . [ label "maxfreq" . store number ] + . space + . [ label "maxwander" . store number ] + . ( space . [ key "leaponly" ] )? + . eol ] + + (* View: tempcomp + tempcomp has specific syntax + *) + let tempcomp = [ Util.indent . key "tempcomp" + . space + . [ label "sensorfile" . store path ] + . space + . [ label "interval" . store number ] + . space + . ( [ label "t0" . store number ] . space + . [ label "k0" . store number ] . space + . [ label "k1" . store number ] . space + . [ label "k2" . store number ] + | [ label "pointfile" . store path ] ) + . eol ] + (************************************************************************ * Group: Final lense summary ************************************************************************) (* View: settings * All supported chrony settings *) -let settings = host_list | log_list | bcast | fdrift | istepslew - | local | email | makestep | maxchange | kv | all_flags +let settings = host_list | allowdeny | log_list | bcast | fdrift | istepslew + | local | email | makestep | maxchange | refclock | smoothtime + | ratelimit | tempcomp | kv | all_flags (* * View: lns diff --git a/lenses/cron.aug b/lenses/cron.aug index 4787814..6dcf4b4 100644 --- a/lenses/cron.aug +++ b/lenses/cron.aug @@ -145,6 +145,7 @@ let lns = ( empty | comment | shellvar | entry )* let filter = incl "/etc/cron.d/*" . incl "/etc/crontab" . + incl "/etc/crontabs/*" . excl "/etc/cron.d/at.allow" . excl "/etc/cron.d/at.deny" . excl "/etc/cron.d/cron.allow" . diff --git a/lenses/csv.aug b/lenses/csv.aug new file mode 100644 index 0000000..d31f6e2 --- /dev/null +++ b/lenses/csv.aug @@ -0,0 +1,51 @@ +(* +Module: CSV + Generic CSV lens collection + +Author: Raphael Pinson + +About: Reference + https://tools.ietf.org/html/rfc4180 + +About: License + This file is licenced under the LGPL v2+, like the rest of Augeas. + +About: Lens Usage + To be documented + +About: Configuration files + +About: Examples + The file contains various examples and tests. + +Caveats: + No support for files without an ending CRLF +*) +module CSV = + +(* View: eol *) +let eol = Util.del_str "\n" + +(* View: comment *) +let comment = Util.comment + | [ del /^#[ \t]*\r?\n/ "#\n" ] + +(* View: entry + An entry of fields, quoted or not *) +let entry (sep_str:string) = + let field = [ seq "field" . store (/[^"#\r\n]/ - sep_str)* ] + | [ seq "field" . store /("[^"#]*")+/ ] + in let sep = Util.del_str sep_str + in [ seq "entry" . counter "field" . Build.opt_list field sep . eol ] + +(* View: lns + The generic lens, taking the separator as a parameter *) +let lns_generic (sep:string) = (comment | entry sep)* + +(* View: lns + The comma-separated value lens *) +let lns = lns_generic "," + +(* View: lns_semicol + A semicolon-separated value lens *) +let lns_semicol = lns_generic ";" diff --git a/lenses/dhclient.aug b/lenses/dhclient.aug index 692985d..722e7f9 100644 --- a/lenses/dhclient.aug +++ b/lenses/dhclient.aug @@ -95,7 +95,8 @@ let stmt_hash_re = "send" let stmt_hash = [ key stmt_hash_re . sep_spc - . [ key word . sep_spc . (sto_to_spc_noeval|rfc_code|eval) ] + . ( [ key word . sep_spc . sto_to_spc_noeval ] + | [ key word . sep_spc . (rfc_code|eval) ] ) . sep_scl . comment_or_eol ] diff --git a/lenses/erlang.aug b/lenses/erlang.aug index 2d73d25..d219f50 100644 --- a/lenses/erlang.aug +++ b/lenses/erlang.aug @@ -132,6 +132,18 @@ let tuple (one:lens) (two:lens) = . [ label "value" . two ] . lspace rbrace ] +(* View: tuple3 + A tuple of 3 values *) +let tuple3 (one:lens) (two:lens) (three:lens) = + [ rspace lbrace + . label "tuple" + . [ label "value" . one ] + . lrspace comma + . [ label "value" . two ] + . lrspace comma + . [ label "value" . three ] + . lspace rbrace ] + (* View: list A list of lenses *) let list (kw:regexp) (lns:lens) = diff --git a/lenses/fstab.aug b/lenses/fstab.aug index bceaddd..7087c34 100644 --- a/lenses/fstab.aug +++ b/lenses/fstab.aug @@ -32,8 +32,8 @@ module Fstab = . Util.comment_or_eol ] let lns = ( empty | comment | record ) * - let filter = (incl "/etc/fstab") - . (incl "/etc/mtab") + let filter = incl "/etc/fstab" + . incl "/etc/mtab" let xfm = transform lns filter diff --git a/lenses/group.aug b/lenses/group.aug index cb515ed..be16892 100644 --- a/lenses/group.aug +++ b/lenses/group.aug @@ -22,8 +22,7 @@ let colon = Sep.colon let comma = Sep.comma let sto_to_spc = store Rx.space_in -let sto_to_col = store /[^:\n]+/ -let sto_to_eol = store /([^ \t\n].*[^ \t\n]|[^ \t\n])/ +let sto_to_col = Passwd.sto_to_col let word = Rx.word let password = /[A-Za-z0-9_.!*-]*/ @@ -43,8 +42,8 @@ let entry = Build.key_value_line word colon params let nisdefault = let overrides = colon - . [ label "password" . store word? . colon ] - . [ label "gid" . store integer? . colon ] + . [ label "password" . store password? . colon ] + . [ label "gid" . store integer? . colon ] . user_list? in [ dels "+" . label "@nisdefault" . overrides? . eol ] diff --git a/lenses/host_conf.aug b/lenses/host_conf.aug index 00a3492..294975b 100644 --- a/lenses/host_conf.aug +++ b/lenses/host_conf.aug @@ -44,7 +44,7 @@ let bool_warn (kw:regexp) = Build.key_value_line kw Sep.space sto_bool_warn (* View: list A list of items *) let list (kw:regexp) (elem:string) = - let list_elems = Build.opt_list [seq elem . store Rx.word] (Sep.comma) in + let list_elems = Build.opt_list [seq elem . store Rx.word] (Sep.comma . Sep.opt_space) in Build.key_value_line kw Sep.space list_elems (* View: trim *) diff --git a/lenses/httpd.aug b/lenses/httpd.aug index 9b50a8f..7a5129b 100644 --- a/lenses/httpd.aug +++ b/lenses/httpd.aug @@ -45,13 +45,12 @@ autoload xfm let dels (s:string) = del s s (* deal with continuation lines *) -let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)/ " " - -let sep_osp = Sep.opt_space +let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)+/ " " +let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)*/ "" let sep_eq = del /[ \t]*=[ \t]*/ "=" let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/ -let word = /[a-zA-Z][a-zA-Z0-9._-]*/ +let word = /[a-z][a-z0-9._-]*/i let comment = Util.comment let eol = Util.doseol @@ -59,13 +58,18 @@ let empty = Util.empty_dos let indent = Util.indent (* borrowed from shellvars.aug *) -let char_arg_dir = /[^\\ '"\t\r\n]|\\\\"|\\\\'/ -let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/ +let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'|\\\\ / +let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'|\\\\ / +let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/ + let cdot = /\\\\./ let cl = /\\\\\n/ let dquot = let no_dquot = /[^"\\\r\n]/ in /"/ . (no_dquot|cdot|cl)* . /"/ +let dquot_msg = + let no_dquot = /([^ \t"\\\r\n]|[^"\\\r\n]+[^ \t"\\\r\n])/ + in /"/ . (no_dquot|cdot|cl)* let squot = let no_squot = /[^'\\\r\n]/ in /'/ . (no_squot|cdot|cl)* . /'/ @@ -76,12 +80,24 @@ let comp = /[<>=]?=/ *****************************************************************) let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ] +(* message argument starts with " but ends at EOL *) +let arg_dir_msg = [ label "arg" . store dquot_msg ] let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ] +let arg_wl = [ label "arg" . store (char_arg_wl+|dquot|squot) ] + +(* comma-separated wordlist as permitted in the SSLRequire directive *) +let arg_wordlist = + let wl_start = Util.del_str "{" in + let wl_end = Util.del_str "}" in + let wl_sep = del /[ \t]*,[ \t]*/ ", " + in [ label "wordlist" . wl_start . arg_wl . (wl_sep . arg_wl)* . wl_end ] let argv (l:lens) = l . (sep_spc . l)* -let directive = [ indent . label "directive" . store word . - (sep_spc . argv arg_dir)? . eol ] +let directive = + (* arg_dir_msg may be the last or only argument *) + let dir_args = (argv (arg_dir|arg_wordlist) . (sep_spc . arg_dir_msg)?) | arg_dir_msg + in [ indent . label "directive" . store word . (sep_spc . dir_args)? . eol ] let section (body:lens) = (* opt_eol includes empty lines *) @@ -89,11 +105,17 @@ let section (body:lens) = let inner = (sep_spc . argv arg_sec)? . sep_osp . dels ">" . opt_eol . ((body|comment) . (body|empty|comment)*)? . indent . dels "" ">" . eol ] + let kword = key (word - /perl/i) in + let dword = del (word - /perl/i) "a" in + [ indent . dels "<" . square kword inner dword . del />[ \t\n\r]*/ ">\n" ] + +let perl_section = [ indent . label "Perl" . del //i "" + . store /[^<]*/ + . del /<\/perl>/i "" . eol ] + let rec content = section (content|directive) + | perl_section let lns = (content|directive|comment|empty)* @@ -104,6 +126,7 @@ let filter = (incl "/etc/apache2/apache2.conf") . (incl "/etc/apache2/conf-available/*.conf") . (incl "/etc/apache2/mods-available/*") . (incl "/etc/apache2/sites-available/*") . + (incl "/etc/apache2/vhosts.d/*.conf") . (incl "/etc/httpd/conf.d/*.conf") . (incl "/etc/httpd/httpd.conf") . (incl "/etc/httpd/conf/httpd.conf") . diff --git a/lenses/inputrc.aug b/lenses/inputrc.aug index 67032ac..3902528 100644 --- a/lenses/inputrc.aug +++ b/lenses/inputrc.aug @@ -27,7 +27,7 @@ autoload xfm (* View: entry An inputrc mapping entry *) let entry = - let mapping = [ label "mapping" . store Rx.word ] + let mapping = [ label "mapping" . store /[A-Za-z0-9_."\*\/+\,\\-]+/ ] in [ label "entry" . Util.del_str "\"" . store /[^" \t\n]+/ . Util.del_str "\":" . Sep.space @@ -45,6 +45,8 @@ let variable = [ Util.del_str "set" . Sep.space let rec condition = [ Util.del_str "$if" . label "@if" . Sep.space . store Rx.space_in . Util.eol . (Util.empty | Util.comment | condition | variable | entry)* + . [ Util.del_str "$else" . label "@else" . Util.eol + . (Util.empty | Util.comment | condition | variable | entry)* ] ? . Util.del_str "$endif" . Util.eol ] (* View: lns diff --git a/lenses/interfaces.aug b/lenses/interfaces.aug index 2051213..4d68a62 100644 --- a/lenses/interfaces.aug +++ b/lenses/interfaces.aug @@ -38,7 +38,7 @@ let stanza_param (l:string) = [ sep_spc . label l . sto_to_spc ] (* Define reseverved words and multi-value options*) let stanza_word = - /(source|iface|auto|allow-[a-z-]+|mapping|bond-slaves|bridge-ports)/ + /(source(-directory)?|iface|auto|allow-[a-z-]+|mapping|bond-slaves|bridge-ports)/ (* Define stanza option indentation *) let stanza_indent = del /[ \t]*/ " " @@ -100,6 +100,12 @@ let iface = [ Util.indent let source = [ key "source" . sep_spc . sto_to_eol ] (************************************************************************ + * SOURCE-DIRECTORY + *************************************************************************) + +let source_directory = [ key "source-directory" . sep_spc . sto_to_eol ] + +(************************************************************************ * STANZAS *************************************************************************) @@ -109,7 +115,7 @@ let source = [ key "source" . sep_spc . sto_to_eol ] come after an auto or hotplug stanza, otherwise they are considered part of a iface or mapping block *) -let stanza_single = (auto|allow|source) . (comment|empty)* +let stanza_single = (auto|allow|source|source_directory) . (comment|empty)* let stanza_multi = iface|mapping (************************************************************************ diff --git a/lenses/json.aug b/lenses/json.aug index 2645806..f47f9b0 100644 --- a/lenses/json.aug +++ b/lenses/json.aug @@ -11,42 +11,37 @@ module Json = (* String ::= "\"" Char* "\"" *) (* Number ::= /-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/ *) -let spc = /[ \t\n]*/ -let ws = del spc "" -let eol = del spc "\n" -let delim (c:string) (d:string) = del (c . spc) d -let dels (s:string) = del s s +let ws = del /[ \t\n]*/ "" +let comment = Util.empty_c_style | Util.comment_c_style | Util.comment_multiline +let comments = comment* . Sep.opt_space -let comma = delim "," "," -let colon = delim ":" ":" -let lbrace = delim "{" "{" -let rbrace = delim "}" "}" -let lbrack = delim "[" "[" -let rbrack = delim "]" "]" +let comma = Util.del_str "," . comments +let colon = Util.del_str ":" . comments +let lbrace = Util.del_str "{" . comments +let rbrace = Util.del_str "}" +let lbrack = Util.del_str "[" . comments +let rbrack = Util.del_str "]" -let str_store = - let q = del "\"" "\"" in - q . store /[^"]*/ . q . ws (* " Emacs, relax *) +let str_store = Quote.dquote . store /([^\\\\"]|\\\\("|n|r|t|\\\\))*/ . Quote.dquote (* " Emacs, relax *) -let number = [ label "number" . store /-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/ . ws ] -let str = [ label "string" . str_store ] +let number = [ label "number" . store /-?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?/ + . comments ] +let str = [ label "string" . str_store . comments ] -let const (r:regexp) = [ label "const" . store r . ws ] - -let value0 = str | number | const /true|false|null/ +let const (r:regexp) = [ label "const" . store r . comments ] let fix_value (value:lens) = - let array = [ label "array" . lbrack . (Build.opt_list value comma)? . rbrack ] in - let pair = [ label "entry" . str_store . colon . value ] in - let obj = [ label "dict" . lbrace . (Build.opt_list pair comma)? . rbrace ] in - (str | number | obj | array | const /true|false|null/) - -(* Typecheck finitely deep nesting *) -let value1 = fix_value value0 -let value2 = fix_value value1 + let array = [ label "array" . lbrack + . ( ( Build.opt_list value comma . rbrack . comments ) + | (rbrack . ws) ) ] + in let pair = [ label "entry" . str_store . ws . colon . value ] + in let obj = [ label "dict" . lbrace + . ( ( Build.opt_list pair comma. rbrace . comments ) + | (rbrace . ws ) ) ] + in (str | number | obj | array | const /true|false|null/) (* Process arbitrarily deeply nested JSON objects *) let rec rlns = fix_value rlns -let lns = ws . rlns +let lns = comments . rlns diff --git a/lenses/keepalived.aug b/lenses/keepalived.aug index 9fd02ff..abe65dd 100644 --- a/lenses/keepalived.aug +++ b/lenses/keepalived.aug @@ -73,6 +73,9 @@ let sto_word = store word (* View: sto_num *) let sto_num = store Rx.relinteger +(* View: sto_ipv6 *) +let sto_ipv6 = store Rx.ipv6 + (* View: sto_to_eol *) let sto_to_eol = store /[^#! \t\n][^#!\n]*[^#! \t\n]|[^#! \t\n]/ @@ -87,7 +90,7 @@ let flag (kw:regexp) = [ indent . key kw . comment_or_eol ] An IP port pair *) let ip_port = [ label "ip" . sto_word ] . sep_spc . [ label "port" . sto_num ] -(* View: lens_block +(* View: lens_block A generic block with a title lens. The definition is very similar to Build.block_newlines but uses a different type of . *) @@ -99,13 +102,13 @@ let lens_block (title:lens) (sto:lens) = A simple block with just a block title *) let block (kw:regexp) (sto:lens) = lens_block (key kw) sto -(* View: named_block +(* View: named_block A block with a block title and name *) let named_block (kw:string) (sto:lens) = lens_block (key kw . sep_spc . sto_word) sto (* View: named_block_arg_title A title lens for named_block_arg *) -let named_block_arg_title (kw:string) (name:string) (arg:string) = +let named_block_arg_title (kw:string) (name:string) (arg:string) = key kw . sep_spc . [ label name . sto_word ] . sep_spc @@ -113,7 +116,7 @@ let named_block_arg_title (kw:string) (name:string) (arg:string) = (* View: named_block_arg A block with a block title, a name and an argument *) -let named_block_arg (kw:string) (name:string) (arg:string) (sto:lens) = +let named_block_arg (kw:string) (name:string) (arg:string) (sto:lens) = lens_block (named_block_arg_title kw name arg) sto @@ -128,12 +131,14 @@ let email = [ indent . label "email" . sto_email_addr . comment_or_eol ] (* View: global_defs_field Possible fields in the global_defs block *) let global_defs_field = - let word_re = "smtp_server"|"lvs_id"|"router_id" + let word_re = "smtp_server"|"lvs_id"|"router_id"|"vrrp_mcast_group4" + in let ipv6_re = "vrrp_mcast_group6" in let num_re = "smtp_connect_timeout" in block "notification_email" email | field "notification_email_from" sto_email_addr | field word_re sto_word | field num_re sto_num + | field ipv6_re sto_ipv6 (* View: global_defs A global_defs block *) @@ -173,7 +178,7 @@ let static_routes = block "static_ipaddress" static_ipaddress_field | block "static_routes" static_routes_field -(* View: global_conf +(* View: global_conf A global configuration entry *) let global_conf = global_defs | static_routes @@ -183,7 +188,12 @@ let global_conf = global_defs | static_routes *************************************************************************) (*View: vrrp_sync_group_field *) -let vrrp_sync_group_field = block "group" [ indent . key word . comment_or_eol ] +let vrrp_sync_group_field = + let to_eol_re = /notify(_master|_backup|_fault)?/ + in let flag_re = "smtp_alert" + in field to_eol_re sto_to_eol + | flag flag_re + | block "group" [ indent . key word . comment_or_eol ] (* View: vrrp_sync_group *) let vrrp_sync_group = named_block "vrrp_sync_group" vrrp_sync_group_field @@ -191,9 +201,9 @@ let vrrp_sync_group = named_block "vrrp_sync_group" vrrp_sync_group_field (* View: vrrp_instance_field *) let vrrp_instance_field = let word_re = "state" | "interface" | "lvs_sync_daemon_interface" - in let num_re = "virtual_router_id" | "priority" | "advert_int" | "garp_master_delay" - in let to_eol_re = /notify_(master|backup|fault)/ - in let flag_re = "smtp_alert" | "nopreempt" | "ha_suspend" | "debug" + in let num_re = "virtual_router_id" | "priority" | "advert_int" | /garp_master_(delay|repeat|refresh|refresh_repeat)/ + in let to_eol_re = /notify(_master|_backup|_fault)?/ | /(mcast|unicast)_src_ip/ + in let flag_re = "smtp_alert" | "nopreempt" | "ha_suspend" | "debug" | "use_vmac" | "vmac_xmit_base" | "native_ipv6" | "dont_track_primary" | "preempt_delay" in field word_re sto_word | field num_re sto_num | field to_eol_re sto_to_eol @@ -203,13 +213,14 @@ let vrrp_instance_field = ) | block "virtual_ipaddress" static_ipaddress_field | block /track_(interface|script)/ ( flag word ) + | block "unicast_peer" static_ipaddress_field (* View: vrrp_instance *) let vrrp_instance = named_block "vrrp_instance" vrrp_instance_field (* View: vrrp_script_field *) let vrrp_script_field = - let num_re = "interval" | "weight" + let num_re = "interval" | "weight" | "fall" | "raise" in let to_eol_re = "script" in field to_eol_re sto_to_eol | field num_re sto_num diff --git a/lenses/known_hosts.aug b/lenses/known_hosts.aug index 357fdf3..6d027d6 100644 --- a/lenses/known_hosts.aug +++ b/lenses/known_hosts.aug @@ -5,8 +5,7 @@ Module: Known_Hosts Author: Raphaël Pinson About: Reference - This lens ensures that conf files included in ActiveMQ /FuseMQ are properly - handled by Augeas. + This lens manages OpenSSH's known_hosts files. See `man 8 sshd` for reference. About: License This file is licenced under the LGPL v2+, like the rest of Augeas. @@ -30,17 +29,35 @@ module Known_Hosts = autoload xfm + +(* View: marker + The marker is optional, but if it is present then it must be one of + “@cert-authority”, to indicate that the line contains a certification + authority (CA) key, or “@revoked”, to indicate that the key contained + on the line is revoked and must not ever be accepted. + Only one marker should be used on a key line. +*) +let marker = [ key /@(revoked|cert-authority)/ . Sep.space ] + + +(* View: type + Bits, exponent, and modulus are taken directly from the RSA host key; + they can be obtained, for example, from /etc/ssh/ssh_host_key.pub. + The optional comment field continues to the end of the line, and is not used. +*) +let type = [ label "type" . store Rx.neg1 ] + + (* View: entry A known_hosts entry *) let entry = - let alias = [ label "alias" . store Rx.neg1 ] - in [ Util.indent . seq "entry" . store Rx.neg1 + let alias = [ label "alias" . store Rx.neg1 ] + in let key = [ label "key" . store Rx.neg1 ] + in [ Util.indent . seq "entry" . marker? + . store Rx.neg1 . (Sep.comma . Build.opt_list alias Sep.comma)? - . Sep.space - . [ label "type" . store Rx.no_spaces ] - . Sep.space - . [ label "key" . store Rx.no_spaces ] - . Util.eol ] + . Sep.space . type . Sep.space . key + . Util.comment_or_eol ] (* View: lns The known_hosts lens *) diff --git a/lenses/logrotate.aug b/lenses/logrotate.aug index 7385392..ceaab95 100644 --- a/lenses/logrotate.aug +++ b/lenses/logrotate.aug @@ -78,6 +78,7 @@ module Logrotate = | value_to_eol "extension" word | select_to_eol "dateext" /(no)?dateext/ | value_to_eol "dateformat" word + | flag_to_eol "dateyesterday" | value_to_eol "compresscmd" word | value_to_eol "uncompresscmd" word | value_to_eol "compressext" word diff --git a/lenses/masterpasswd.aug b/lenses/masterpasswd.aug new file mode 100644 index 0000000..06e4e0f --- /dev/null +++ b/lenses/masterpasswd.aug @@ -0,0 +1,148 @@ +(* + Module: MasterPasswd + Parses /etc/master.passwd + + Author: Matt Dainty + + About: Reference + - man 5 master.passwd + + Each line in the master.passwd file represents a single user record, whose + colon-separated attributes correspond to the members of the passwd struct + +*) + +module MasterPasswd = + + autoload xfm + +(************************************************************************ + * Group: USEFUL PRIMITIVES + *************************************************************************) + +(* Group: Comments and empty lines *) + +let eol = Util.eol +let comment = Util.comment +let empty = Util.empty +let dels = Util.del_str + +let word = Rx.word +let integer = Rx.integer + +let colon = Sep.colon + +let sto_to_eol = Passwd.sto_to_eol +let sto_to_col = Passwd.sto_to_col +(* Store an empty string if nothing matches *) +let sto_to_col_or_empty = Passwd.sto_to_col_or_empty + +(************************************************************************ + * Group: ENTRIES + *************************************************************************) + +let username = /[_.A-Za-z0-9][-_.A-Za-z0-9]*\$?/ + +(* View: password + pw_passwd *) +let password = [ label "password" . sto_to_col? . colon ] + +(* View: uid + pw_uid *) +let uid = [ label "uid" . store integer . colon ] + +(* View: gid + pw_gid *) +let gid = [ label "gid" . store integer . colon ] + +(* View: class + pw_class *) +let class = [ label "class" . sto_to_col? . colon ] + +(* View: change + pw_change *) +let change_date = [ label "change_date" . store integer? . colon ] + +(* View: expire + pw_expire *) +let expire_date = [ label "expire_date" . store integer? . colon ] + +(* View: name + pw_gecos; the user's full name *) +let name = [ label "name" . sto_to_col? . colon ] + +(* View: home + pw_dir *) +let home = [ label "home" . sto_to_col? . colon ] + +(* View: shell + pw_shell *) +let shell = [ label "shell" . sto_to_eol? ] + +(* View: entry + struct passwd *) +let entry = [ key username + . colon + . password + . uid + . gid + . class + . change_date + . expire_date + . name + . home + . shell + . eol ] + +(* NIS entries *) +let niscommon = [ label "password" . sto_to_col ]? . colon + . [ label "uid" . store integer ]? . colon + . [ label "gid" . store integer ]? . colon + . [ label "class" . sto_to_col ]? . colon + . [ label "change_date" . store integer ]? . colon + . [ label "expire_date" . store integer ]? . colon + . [ label "name" . sto_to_col ]? . colon + . [ label "home" . sto_to_col ]? . colon + . [ label "shell" . sto_to_eol ]? + +let nisentry = + let overrides = + colon + . niscommon in + [ dels "+@" . label "@nis" . store username . overrides . eol ] + +let nisuserplus = + let overrides = + colon + . niscommon in + [ dels "+" . label "@+nisuser" . store username . overrides . eol ] + +let nisuserminus = + let overrides = + colon + . niscommon in + [ dels "-" . label "@-nisuser" . store username . overrides . eol ] + +let nisdefault = + let overrides = + colon + . [ label "password" . sto_to_col_or_empty . colon ] + . [ label "uid" . store integer? . colon ] + . [ label "gid" . store integer? . colon ] + . [ label "class" . sto_to_col? . colon ] + . [ label "change_date" . store integer? . colon ] + . [ label "expire_date" . store integer? . colon ] + . [ label "name" . sto_to_col? . colon ] + . [ label "home" . sto_to_col? . colon ] + . [ label "shell" . sto_to_eol? ] in + [ dels "+" . label "@nisdefault" . overrides? . eol ] + +(************************************************************************ + * LENS + *************************************************************************) + +let lns = (comment|empty|entry|nisentry|nisdefault|nisuserplus|nisuserminus) * + +let filter = incl "/etc/master.passwd" + +let xfm = transform lns filter diff --git a/lenses/multipath.aug b/lenses/multipath.aug index 6c72ab2..62d7f81 100644 --- a/lenses/multipath.aug +++ b/lenses/multipath.aug @@ -45,6 +45,10 @@ let common_setting = |kv "rr_weight" /priorities|uniform/ |kv "no_path_retry" (Rx.integer | /fail|queue/) |kv /rr_min_io(_rq)?/ Rx.integer + |kv "flush_on_last_del" /yes|no/ + |kv "reservation_key" Rx.word + |kv "delay_watch_checks" (Rx.integer|"no") + |kv "delay_wait_checks" (Rx.integer|"no") let default_setting = kv "polling_interval" Rx.integer @@ -55,9 +59,15 @@ let default_setting = |kv "fast_io_fail_tmo" Rx.integer |kv "verbosity" /[0-6]/ |kv "reassign_maps" /yes|no/ - (* These are not in the manpage but in the example multipath.conf *) |kv "prio" Rx.word |kv "max_fds" Rx.integer + |kv "find_multipaths" /yes|no/ + |kv "checker_timeout" Rx.integer + |kv "hwtable_regex_match" /yes|no/ + |kv "reload_readwrite" /yes|no/ + |kv "replace_wwid_whitespace" /yes|no/ + |kv "force_sync" /yes|no/ + |kv "config_dir" Rx.fspath (* SUSE extensions *) |kv "async_timeout" Rx.integer |kv "max_polling_interval" Rx.integer @@ -74,7 +84,6 @@ let default_setting = |kv "log_checker_err" Rx.word |kv "retain_attached_hw_handler" /yes|no/ |kv "detect_prio" /yes|no/ - |kv "flush_on_last_del" /yes|no/ (* A device subsection *) let device = @@ -117,4 +126,5 @@ let devices = let lns = (comment|empty|defaults|blacklist|devices|multipaths)* -let xfm = transform lns (incl "/etc/multipath.conf") +let xfm = transform lns (incl "/etc/multipath.conf" . + incl "/etc/multipath/conf.d/*.conf") diff --git a/lenses/mysql.aug b/lenses/mysql.aug index eaeb34e..1b8c315 100644 --- a/lenses/mysql.aug +++ b/lenses/mysql.aug @@ -41,6 +41,7 @@ let lns = (comment|IniFile.empty)* . (record|includedir)* let filter = (incl "/etc/mysql/my.cnf") . (incl "/etc/mysql/conf.d/*.cnf") . (incl "/etc/my.cnf") + . (incl "/etc/my.cnf.d/*.cnf") let xfm = transform lns filter diff --git a/lenses/nginx.aug b/lenses/nginx.aug index 082fd91..94ffa3c 100644 --- a/lenses/nginx.aug +++ b/lenses/nginx.aug @@ -31,6 +31,9 @@ module Nginx = autoload xfm +(* Variable: word *) +let word = /[A-Za-z0-9_.:-]+/ + (* Variable: block_re The keywords reserved for block entries *) let block_re = "http" | "events" | "server" | "mail" | "stream" @@ -42,9 +45,19 @@ let block_re_all = block_re | "if" | "location" | "geo" | "map" (* View: simple A simple entry *) let simple = - let kw = Rx.word - block_re_all + let kw = word - block_re_all + in let mask = [ label "mask" . Util.del_str "/" . store Rx.integer ] in let sto = store /[^ \t\n;][^;]*/ . Sep.semicolon - in [ Util.indent . key kw . Sep.space . sto . (Util.eol|Util.comment_eol) ] + in [ Util.indent . key kw . mask? . Sep.space . sto . (Util.eol|Util.comment_eol) ] + +(* View: server + A simple server entry *) +let server = + [ Util.indent . label "@server" . Util.del_str "server" + . [ Sep.space . label "@address" . store word ] + . [ Sep.space . key word . (Sep.equal . store word)? ]* + . Sep.semicolon + . (Util.eol|Util.comment_eol) ] let arg (name:string) (rx:regexp) = [ label name . Sep.space . store rx ] @@ -98,7 +111,7 @@ let block (entry : lens) = . Build.block_newlines entry Util.comment . Util.eol ] -let rec directive = simple | block directive +let rec directive = simple | server | block directive (* View: lns *) let lns = ( Util.comment | Util.empty | directive )* diff --git a/lenses/ntp.aug b/lenses/ntp.aug index c9a79a1..c6d35b1 100644 --- a/lenses/ntp.aug +++ b/lenses/ntp.aug @@ -121,12 +121,17 @@ module Ntp = let arg = [ key arg_names . sep_spc . store Rx.decimal ] in [ key "tos" . (sep_spc . arg)* . eol ] + let interface = + let action = [ label "action" . store /listen|ignore|drop/ ] + in let addresses = [ label "addresses" . store Rx.word ] + in [ key "interface" . sep_spc . action . sep_spc . addresses . eol ] + (* Define lens *) let lns = ( comment | empty | command_record | fudge_record | restrict_record | simple_settings | statistics_record | filegen_record | broadcastclient - | auth_command | tinker | tos)* + | auth_command | tinker | tos | interface)* let filter = (incl "/etc/ntp.conf") diff --git a/lenses/openshift_quickstarts.aug b/lenses/openshift_quickstarts.aug index a732154..da248d5 100644 --- a/lenses/openshift_quickstarts.aug +++ b/lenses/openshift_quickstarts.aug @@ -33,11 +33,8 @@ About: Examples module OpenShift_Quickstarts = autoload xfm -(* View: json *) -let json = Json.lns - (* View: lns *) -let lns = (Util.empty | json )* +let lns = Json.lns (* Variable: filter *) let filter = incl "/etc/openshift/quickstarts.json" diff --git a/lenses/openvpn.aug b/lenses/openvpn.aug index 4771cb1..607e1d6 100644 --- a/lenses/openvpn.aug +++ b/lenses/openvpn.aug @@ -1,7 +1,11 @@ (* OpenVPN module for Augeas Author: Raphael Pinson + Author: Justin Akers Reference: http://openvpn.net/index.php/documentation/howto.html + Reference: https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage + + TODO: Inline file support *) @@ -18,17 +22,37 @@ let indent = Util.indent (* Define separators *) let sep = Util.del_ws_spc -(* Define value regexps *) -let ip_re = Rx.ipv4 +(* Define value regexps. + Custom simplified ipv6 used instead of Rx.ipv6 as the augeas Travis instances + are limited to 2GB of memory. Using 'ipv6_re = Rx.ipv6' consumes an extra + 2GB of memory and thus the test is OOM-killed. +*) +let ipv6_re = /[0-9A-Fa-f:]+/ +let ipv4_re = Rx.ipv4 +let ip_re = ipv4_re|ipv6_re let num_re = Rx.integer let fn_re = /[^#; \t\n][^#;\n]*[^#; \t\n]|[^#; \t\n]/ +let fn_safe_re = /[^#; \t\r\n]+/ let an_re = /[a-z][a-z0-9_-]*/ +let hn_re = Rx.hostname +let port_re = /[0-9]+/ +let host_re = ip_re|hn_re +let proto_re = /(tcp|udp)/ +let proto_ext_re = /(udp|tcp-client|tcp-server)/ +let alg_re = /(none|[A-Za-z][A-Za-z0-9-]+)/ +let ipv6_bits_re = ipv6_re . /\/[0-9]+/ (* Define store aliases *) let ip = store ip_re let num = store num_re let filename = store fn_re +let filename_safe = store fn_safe_re +let hostname = store hn_re let sto_to_dquote = store /[^"\n]+/ (* " Emacs, relax *) +let port = store port_re +let host = store host_re +let proto = store proto_re +let proto_ext = store proto_ext_re (* define comments and empty lines *) let comment = Util.comment_generic /[ \t]*[;#][ \t]*/ "# " @@ -40,12 +64,15 @@ let empty = Util.empty (************************************************************************ * SINGLE VALUES * - * - local => IP + * - local => IP|hostname * - port => num - * - proto => tcp|udp + * - proto => udp|tcp-client|tcp-server + * - proto-force => udp|tcp + * - mode => p2p|server * - dev => (tun|tap)\d* - * - dev-node => MyTap + * - dev-node => filename * - ca => filename + * - config => filename * - cert => filename * - key => filename * - dh => filename @@ -63,68 +90,189 @@ let empty = Util.empty * - mute => num * - fragment => num * - mssfix => num + * - connect-retry num + * - connect-retry-max num + * - connect-timeout num + * - http-proxy-timeout num + * - max-routes num * - ns-cert-type => "server" * - resolv-retry => "infinite" * - script-security => [0-3] (execve|system)? + * - ipchange => command + * - topology => type *************************************************************************) -let single_ip = "local" +let single_host = "local" | "tls-remote" +let single_ip = "lladdr" +let single_ipv6_bits = "iroute-ipv6" + | "server-ipv6" + | "ifconfig-ipv6-pool" let single_num = "port" | "max-clients" | "verb" - | "mute" + | "mute" | "fragment" | "mssfix" -let single_fn = "ca" - | "cert" - | "key" - | "dh" - | "ifconfig-pool-persist" - | "learn-address" - | "status" - | "log" - | "log-append" - | "client-config-dir" + | "connect-retry" + | "connect-retry-max" + | "connect-timeout" + | "http-proxy-timeout" + | "resolv-retry" + | "lport" + | "rport" + | "max-routes" + | "max-routes-per-client" + | "route-metric" + | "tun-mtu" + | "tun-mtu-extra" + | "shaper" + | "ping" + | "ping-exit" + | "ping-restart" + | "sndbuf" + | "rcvbuf" + | "txqueuelen" + | "link-mtu" + | "nice" + | "management-log-cache" + | "bcast-buffers" + | "tcp-queue-limit" + | "server-poll-timeout" + | "keysize" + | "pkcs11-pin-cache" + | "tls-timeout" + | "reneg-bytes" + | "reneg-pkts" + | "reneg-sec" + | "hand-window" + | "tran-window" +let single_fn = "ca" + | "cert" + | "extra-certs" + | "config" + | "key" + | "dh" + | "log" + | "log-append" + | "client-config-dir" + | "dev-node" + | "cd" + | "chroot" + | "writepid" + | "client-config-dir" + | "tmp-dir" + | "replay-persist" + | "ca" + | "capath" + | "pkcs12" + | "pkcs11-id" + | "askpass" + | "tls-export-cert" + | "x509-track" let single_an = "user" | "group" - + | "management-client-user" + | "management-client-group" +let single_cmd = "ipchange" + | "iproute" + | "route-up" + | "route-pre-down" + | "mark" + | "up" + | "down" + | "setcon" + | "echo" + | "client-connect" + | "client-disconnect" + | "learn-address" + | "tls-verify" let single_entry (kw:regexp) (re:regexp) = [ key kw . sep . store re . comment_or_eol ] +let single_opt_entry (kw:regexp) (re:regexp) + = [ key kw . (sep . store re)? .comment_or_eol ] + let single = single_entry single_num num_re | single_entry single_fn fn_re | single_entry single_an an_re - | single_entry "local" ip_re - | single_entry "proto" /(tcp|udp)/ - | single_entry "dev" /(tun|tap)[0-9]*/ - | single_entry "dev-node" "MyTap" - | single_entry "cipher" /[A-Z][A-Z0-9-]*/ - | single_entry "ns-cert-type" "server" + | single_entry single_host host_re + | single_entry single_ip ip_re + | single_entry single_ipv6_bits ipv6_bits_re + | single_entry single_cmd fn_re + | single_entry "proto" proto_ext_re + | single_entry "proto-force" proto_re + | single_entry "mode" /(p2p|server)/ + | single_entry "dev" /(tun|tap)[0-9]*|null/ + | single_entry "dev-type" /(tun|tap)/ + | single_entry "topology" /(net30|p2p|subnet)/ + | single_entry "cipher" alg_re + | single_entry "auth" alg_re | single_entry "resolv-retry" "infinite" | single_entry "script-security" /[0-3]( execve| system)?/ + | single_entry "route-gateway" (host_re|/dhcp/) + | single_entry "mtu-disc" /(no|maybe|yes)/ + | single_entry "remap-usr1" /SIG(HUP|TERM)/ + | single_entry "socket-flags" /(TCP_NODELAY)/ + | single_entry "auth-retry" /(none|nointeract|interact)/ + | single_entry "tls-version-max" Rx.decimal + | single_entry "verify-hash" /([A-Za-z0-9]{2}:)+[A-Za-z0-9]{2}/ + | single_entry "pkcs11-cert-private" /[01]/ + | single_entry "pkcs11-protected-authentication" /[01]/ + | single_entry "pkcs11-private-mode" /[A-Za-z0-9]+/ + | single_entry "key-method" /[12]/ + | single_entry "ns-cert-type" /(client|server)/ + | single_entry "remote-cert-tls" /(client|server)/ + +let single_opt = single_opt_entry "comp-lzo" /(yes|no|adaptive)/ + | single_opt_entry "syslog" fn_re + | single_opt_entry "daemon" fn_re + | single_opt_entry "auth-user-pass" fn_re + | single_opt_entry "explicit-exit-notify" num_re + | single_opt_entry "engine" fn_re + +(************************************************************************ + * DOUBLE VALUES + *************************************************************************) + +let double_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp) + = [ key kw + . sep . [ label a . store aval ] + . sep . [ label b . store bval ] + . comment_or_eol + ] + +let double_secopt_entry (kw:regexp) (a:string) (aval:regexp) (b:string) (bval:regexp) + = [ key kw + . sep . [ label a . store aval ] + . (sep . [ label b . store bval ])? + . comment_or_eol + ] + + +let double = double_entry "keepalive" "ping" num_re "timeout" num_re + | double_entry "hash-size" "real" num_re "virtual" num_re + | double_entry "ifconfig" "local" ip_re "remote" ip_re + | double_entry "connect-freq" "num" num_re "sec" num_re + | double_entry "verify-x509-name" "name" hn_re "type" + /(subject|name|name-prefix)/ + | double_entry "ifconfig-ipv6" "address" ipv6_bits_re "remote" ipv6_re + | double_entry "ifconfig-ipv6-push" "address" ipv6_bits_re "remote" ipv6_re + | double_secopt_entry "iroute" "local" ip_re "netmask" ip_re + | double_secopt_entry "stale-routes-check" "age" num_re "interval" num_re + | double_secopt_entry "ifconfig-pool-persist" + "file" fn_safe_re "seconds" num_re + | double_secopt_entry "secret" "file" fn_safe_re "direction" /[01]/ + | double_secopt_entry "prng" "algorithm" alg_re "nsl" num_re + | double_secopt_entry "replay-window" "window-size" num_re "seconds" num_re (************************************************************************ * FLAGS - * - * - client-to-client - * - duplicate-cn - * - comp-lzo - * - persist-key - * - persist-tun - * - client - * - remote-random - * - nobind - * - mute-replay-warnings - * - http-proxy-retry - * - daemon - * *************************************************************************) let flag_words = "client-to-client" | "duplicate-cn" - | "comp-lzo" | "persist-key" | "persist-tun" | "client" @@ -132,7 +280,77 @@ let flag_words = "client-to-client" | "nobind" | "mute-replay-warnings" | "http-proxy-retry" - | "daemon" + | "socks-proxy-retry" + | "remote-random-hostname" + | "show-proxy-settings" + | "float" + | "bind" + | "nobind" + | "tun-ipv6" + | "ifconfig-noexec" + | "ifconfig-nowarn" + | "route-noexec" + | "route-nopull" + | "allow-pull-fqdn" + | "mtu-test" + | "ping-timer-rem" + | "persist-tun" + | "persist-local-ip" + | "persist-remote-ip" + | "mlock" + | "up-delay" + | "down-pre" + | "up-restart" + | "disable-occ" + | "errors-to-stderr" + | "passtos" + | "suppress-timestamps" + | "fast-io" + | "multihome" + | "comp-noadapt" + | "management-client" + | "management-query-passwords" + | "management-query-proxy" + | "management-query-remote" + | "management-forget-disconnect" + | "management-hold" + | "management-signal" + | "management-up-down" + | "management-client-auth" + | "management-client-pf" + | "push-reset" + | "push-peer-info" + | "disable" + | "ifconfig-pool-linear" + | "client-to-client" + | "duplicate-cn" + | "ccd-exclusive" + | "tcp-nodelay" + | "opt-verify" + | "auth-user-pass-optional" + | "client-cert-not-required" + | "username-as-common-name" + | "pull" + | "key-direction" + | "no-replay" + | "mute-replay-warnings" + | "no-iv" + | "use-prediction-resistance" + | "test-crypto" + | "tls-server" + | "tls-client" + | "pkcs11-id-management" + | "single-session" + | "tls-exit" + | "auth-nocache" + | "show-ciphers" + | "show-digests" + | "show-tls" + | "show-engines" + | "genkey" + | "mktun" + | "rmtun" + let flag_entry (kw:regexp) = [ key kw . comment_or_eol ] @@ -143,83 +361,290 @@ let flag = flag_entry flag_words (************************************************************************ * OTHER FIELDS * - * - server => IP IP + * - server => IP IP [nopool] * - server-bridge => IP IP IP IP - * - route => IP IP + * - route => host host [host [num]] * - push => "string" - * - keepalive => num num * - tls-auth => filename [01] - * - remote => hostname/IP num + * - remote => hostname/IP [num] [(tcp|udp)] * - management => IP num filename + * - http-proxy => host port [filename|keyword] [method] + * - http-proxy-option => (VERSION decimal|AGENT string) + * ... + * and many others * *************************************************************************) -let server = [ key "server" . sep - . [ label "address" . ip ] . sep - . [ label "netmask" . ip ] . comment_or_eol - ] - -let server_bridge = [ key "server-bridge" . sep - . [ label "address" . ip ] . sep - . [ label "netmask" . ip ] . sep - . [ label "start" . ip ] . sep - . [ label "end" . ip ] . comment_or_eol - ] - -let route = [ key "route" . sep - . [ label "address" . ip ] . sep - . [ label "netmask" . ip ] . comment_or_eol - ] +let server = [ key "server" + . sep . [ label "address" . ip ] + . sep . [ label "netmask" . ip ] + . (sep . [ key "nopool" ]) ? + . comment_or_eol + ] + +let server_bridge = + let ip_params = [ label "address" . ip ] . sep + . [ label "netmask" . ip ] . sep + . [ label "start" . ip ] . sep + . [ label "end" . ip ] in + [ key "server-bridge" + . sep . (ip_params|store /(nogw)/) + . comment_or_eol + ] + +let route = + let route_net_kw = store (/(vpn_gateway|net_gateway|remote_host)/|host_re) in + [ key "route" . sep + . [ label "address" . route_net_kw ] + . (sep . [ label "netmask" . store (ip_re|/default/) ] + . (sep . [ label "gateway" . route_net_kw ] + . (sep . [ label "metric" . store (/default/|num_re)] )? + )? + )? + . comment_or_eol + ] + +let route_ipv6 = + let route_net_re = /(vpn_gateway|net_gateway|remote_host)/ in + [ key "route-ipv6" . sep + . [ label "network" . store (route_net_re|ipv6_bits_re) ] + . (sep . [ label "gateway" . store (route_net_re|ipv6_re) ] + . (sep . [ label "metric" . store (/default/|num_re)] )? + )? + . comment_or_eol + ] let push = [ key "push" . sep . Quote.do_dquote sto_to_dquote . comment_or_eol ] -let keepalive = [ key "keepalive" . sep - . [ label "ping" . num ] . sep - . [ label "timeout" . num ] . comment_or_eol - ] - let tls_auth = [ key "tls-auth" . sep . [ label "key" . filename ] . sep . [ label "is_client" . store /[01]/ ] . comment_or_eol ] let remote = [ key "remote" . sep - . [ label "server" . filename ] . sep - . [ label "port" . num ] . comment_or_eol + . [ label "server" . host ] + . (sep . [label "port" . port] + . (sep . [label "proto" . proto]) ? ) ? + . comment_or_eol ] -let http_proxy = [ key "http-proxy" . - ( sep . [ label "server" . store /[A-Za-z0-9._-]+/ ] . - ( sep . [ label "port" . num ] )? )? - . comment_or_eol - ] +let http_proxy = + let auth_method_re = /(none|basic|ntlm)/ in + let auth_method = store auth_method_re in + [ key "http-proxy" + . sep . [ label "server" . host ] + . sep . [ label "port" . port ] + . (sep . [ label "auth" . filename_safe ] + . (sep . [ label "auth-method" . auth_method ]) ? )? + . comment_or_eol + ] + +let http_proxy_option = [ key "http-proxy-option" + . sep . [ label "option" . store /(VERSION|AGENT)/ ] + . sep . [ label "value" . filename ] + . comment_or_eol + ] + +let socks_proxy = [ key "socks-proxy" + . sep . [ label "server" . host ] + . (sep . [ label "port" . port ] + . (sep . [ label "auth" . filename_safe ])? )? + . comment_or_eol + ] + +let port_share = [ key "port-share" + . sep . [ label "host" . host ] + . sep . [ label "port" . port ] + . (sep . [ label "dir" . filename ])? + . comment_or_eol + ] + +let route_delay = [ key "route-delay" + . (sep . [ label "seconds" . num ] + . (sep . [ label "win-seconds" . num ] ) ? + )? + . comment_or_eol + ] -let management = [ key "management" . sep - . [ label "server" . ip ] . sep - . [ label "port" . num ] . sep - . [ label "pwfile" . filename ] . comment_or_eol +let inetd = [ key "inetd" + . (sep . [label "mode" . store /(wait|nowait)/ ] + . (sep . [ label "progname" . filename ] ) ? + )? + . comment_or_eol + ] + +let inactive = [ key "inactive" + . sep . [ label "seconds" . num ] + . (sep . [ label "bytes" . num ] ) ? + . comment_or_eol ] +let client_nat = [ key "client-nat" + . sep . [ label "type" . store /(snat|dnat)/ ] + . sep . [ label "network" . ip ] + . sep . [ label "netmask" . ip ] + . sep . [ label "alias" . ip ] + . comment_or_eol + ] + +let status = [ key "status" + . sep . [ label "file" . filename_safe ] + . (sep . [ label "repeat-seconds" . num ]) ? + . comment_or_eol + ] + +let plugin = [ key "plugin" + . sep . [ label "file" . filename_safe ] + . (sep . [ label "init-string" . filename ]) ? + . comment_or_eol + ] + +let management = [ key "management" . sep + . [ label "server" . ip ] + . sep . [ label "port" . port ] + . (sep . [ label "pwfile" . filename ] ) ? + . comment_or_eol + ] -let other = server - | server_bridge - | route - | push - | keepalive - | tls_auth - | remote - | http_proxy - | management +let auth_user_pass_verify = [ key "auth-user-pass-verify" + . sep . [ Quote.quote_spaces (label "command") ] + . sep . [ label "method" . store /via-(env|file)/ ] + . comment_or_eol + ] + +let static_challenge = [ key "static-challenge" + . sep . [ Quote.quote_spaces (label "text") ] + . sep . [ label "echo" . store /[01]/ ] + . comment_or_eol + ] + +let cryptoapicert = [ key "cryptoapicert" . sep . Quote.dquote + . [ key /[A-Z]+/ . Sep.colon . store /[A-Za-z _-]+/ ] + . Quote.dquote . comment_or_eol + ] + +let setenv = + let envvar = /[^#;\/ \t\n][A-Za-z0-9_-]+/ in + [ key ("setenv"|"setenv-safe") + . sep . [ key envvar . sep . store fn_re ] + . comment_or_eol + ] + +let redirect = + let redirect_flag = /(local|autolocal|def1|bypass-dhcp|bypass-dns|block-local)/ in + let redirect_key = "redirect-gateway" | "redirect-private" in + [ key redirect_key + . (sep . [ label "flag" . store redirect_flag ] ) + + . comment_or_eol + ] + +let tls_cipher = + let ciphername = /[A-Za-z0-9!_-]+/ in + [ key "tls-cipher" . sep + . [label "cipher" . store ciphername] + . (Sep.colon . [label "cipher" . store ciphername])* + . comment_or_eol + ] + +let remote_cert_ku = + let usage = [label "usage" . store /[A-Za-z0-9]{1,2}/] in + [ key "remote-cert-ku" . sep . usage . (sep . usage)* . comment_or_eol ] + +(* FIXME: Surely there's a nicer way to do this *) +let remote_cert_eku = + let oid = [label "oid" . store /[0-9]+\.([0-9]+\.)*[0-9]+/] in + let symbolic = [Quote.do_quote_opt + (label "symbol" . store /[A-Za-z0-9][A-Za-z0-9 _-]*[A-Za-z0-9]/)] in + [ key "remote-cert-eku" . sep . (oid|symbolic) . comment_or_eol ] + +let status_version = [ key "status-version" + . (sep . num) ? + . comment_or_eol + ] + +let ifconfig_pool = [ key "ifconfig-pool" + . sep . [ label "start" . ip ] + . sep . [ label "end" . ip ] + . (sep . [ label "netmask" . ip ])? + . comment_or_eol + ] + +let ifconfig_push = [ key "ifconfig-push" + . sep . [ label "local" . ip ] + . sep . [ label "remote-netmask" . ip ] + . (sep . [ label "alias" . store /[A-Za-z0-9_-]+/ ] )? + . comment_or_eol + ] + +let ignore_unknown_option = [ key "ignore-unknown-option" + . (sep . [ label "opt" . store /[A-Za-z0-9_-]+/ ] ) + + . comment_or_eol + ] + +let tls_version_min = [ key "tls-version-min" + . sep . store Rx.decimal + . (sep . [ key "or-highest" ]) ? + . comment_or_eol + ] + +let crl_verify = [ key "crl-verify" + . sep . filename_safe + . (sep . [ key "dir" ]) ? + . comment_or_eol + ] + +let x509_username_field = + let fieldname = /[A-Za-z0-9_-]+/ in + let extfield = ([key /ext/ . Sep.colon . store fieldname]) in + let subjfield = ([label "subj" . store fieldname]) in + [ key "x509-username-field" + . sep . (extfield|subjfield) + . comment_or_eol + ] + +let other = server + | server_bridge + | route + | push + | tls_auth + | remote + | http_proxy + | http_proxy_option + | socks_proxy + | management + | route_delay + | client_nat + | redirect + | inactive + | setenv + | inetd + | status + | status_version + | plugin + | ifconfig_pool + | ifconfig_push + | ignore_unknown_option + | auth_user_pass_verify + | port_share + | static_challenge + | tls_version_min + | tls_cipher + | cryptoapicert + | x509_username_field + | remote_cert_ku + | remote_cert_eku + | crl_verify + | route_ipv6 (************************************************************************ * LENS & FILTER *************************************************************************) -let lns = ( comment | empty | single | flag | other )* +let lns = ( comment | empty | single | single_opt | double | flag | other )* let filter = (incl "/etc/openvpn/client.conf") . (incl "/etc/openvpn/server.conf") diff --git a/lenses/pg_hba.aug b/lenses/pg_hba.aug index f9b7a0e..a9d165d 100644 --- a/lenses/pg_hba.aug +++ b/lenses/pg_hba.aug @@ -82,6 +82,7 @@ module Pg_Hba = The pg_hba.conf conf file *) let filter = (incl "/var/lib/pgsql/data/pg_hba.conf" . incl "/var/lib/pgsql/*/data/pg_hba.conf" . + incl "/var/lib/postgresql/*/data/pg_hba.conf" . incl "/etc/postgresql/*/*/pg_hba.conf" ) (* View: lns diff --git a/lenses/postgresql.aug b/lenses/postgresql.aug index 5f0470b..4946643 100644 --- a/lenses/postgresql.aug +++ b/lenses/postgresql.aug @@ -71,6 +71,7 @@ let lns = (Util.empty | Util.comment | entry)* (* Variable: filter *) let filter = (incl "/var/lib/pgsql/data/postgresql.conf" . incl "/var/lib/pgsql/*/data/postgresql.conf" . + incl "/var/lib/postgresql/*/data/postgresql.conf" . incl "/etc/postgresql/*/*/postgresql.conf" ) let xfm = transform lns filter diff --git a/lenses/puppetfile.aug b/lenses/puppetfile.aug index 5389bd1..89eeb0b 100644 --- a/lenses/puppetfile.aug +++ b/lenses/puppetfile.aug @@ -26,6 +26,10 @@ module Puppetfile = a comma, optionally preceded or followed by spaces or newlines *) let comma = del /[ \t\n]*,[ \t\n]*/ ", " +(* View: moduledir + The moduledir setting specifies where modules from the Puppetfile will be installed *) +let moduledir = [ Util.indent . key "moduledir" . Sep.space . Quote.any . Util.eol ] + (* View: forge a forge entry *) let forge = [ Util.indent . key "forge" . Sep.space . Quote.any . Util.eol ] @@ -37,7 +41,7 @@ let metadata = [ Util.indent . key "metadata" . Util.eol ] (* View: mod a module entry, with optional version and options *) let mod = - let mod_name = Quote.do_quote (store (Rx.word . /[\/-]/ . Rx.word)) + let mod_name = Quote.do_quote (store ((Rx.word . /[\/-]/)? . Rx.word)) in let version = [ label "@version" . Quote.do_quote (store /[^:\n]+/) ] in let opt = [ Util.del_str ":" . key Rx.word . del /[ \t]*=>[ \t]*/ " => " . Quote.do_quote (store /[^,\n]*/) ] @@ -49,4 +53,4 @@ let mod = (* View: lns the Puppetfile lens *) -let lns = (Util.empty | Util.comment | forge | metadata | mod)* +let lns = (Util.empty | Util.comment | forge | metadata | mod | moduledir )* diff --git a/lenses/rabbitmq.aug b/lenses/rabbitmq.aug index 1d7c89a..7543de6 100644 --- a/lenses/rabbitmq.aug +++ b/lenses/rabbitmq.aug @@ -34,12 +34,14 @@ let listeners = (* View: ssl_options (Incomplete) list of SSL options *) let ssl_options = - let option = Erlang.value /((ca)?cert|key)file/ Erlang.path + let versions_list = Erlang.opt_list (Erlang.make_value Erlang.quoted) + in let option = Erlang.value /((ca)?cert|key)file/ Erlang.path | Erlang.value "verify" Erlang.bare | Erlang.value "verify_fun" Erlang.boolean | Erlang.value /fail_if_no_peer_cert|reuse_sessions/ Erlang.boolean | Erlang.value "depth" Erlang.integer | Erlang.value "password" Erlang.quoted + | Erlang.value "versions" versions_list in Erlang.list "ssl_options" option (* View: disk_free_limit *) @@ -60,6 +62,14 @@ let cluster_nodes = | nodes in Erlang.value "cluster_nodes" value +(* View: cluster_partition_handling + Can be single value or + `{pause_if_all_down, [nodes], ignore | autoheal}` *) +let cluster_partition_handling = + let nodes = Erlang.opt_list (Erlang.make_value Erlang.quoted) + in let value = Erlang.tuple3 Erlang.bare nodes Erlang.bare + | Erlang.bare + in Erlang.value "cluster_partition_handling" value (* View: tcp_listen_options *) let tcp_listen_options = @@ -73,7 +83,7 @@ let parameters = listeners | ssl_options | disk_free_limit | log_levels - | Erlang.value "vm_memory_high_watermark" Erlang.decimal + | Erlang.value /vm_memory_high_watermark(_paging_ratio)?/ Erlang.decimal | Erlang.value "frame_max" Erlang.integer | Erlang.value "heartbeat" Erlang.integer | Erlang.value /default_(vhost|user|pass)/ Erlang.glob @@ -92,7 +102,14 @@ let parameters = listeners | Erlang.value "msg_store_index_module" Erlang.bare | Erlang.value "backing_queue_module" Erlang.bare | Erlang.value "msg_store_file_size_limit" Erlang.integer - | Erlang.value "queue_index_max_ journal_entries" Erlang.integer + | Erlang.value /queue_index_(max_journal_entries|embed_msgs_below)/ Erlang.integer + | cluster_partition_handling + | Erlang.value /(ssl_)?handshake_timeout/ Erlang.integer + | Erlang.value "channel_max" Erlang.integer + | Erlang.value_list "loopback_users" Erlang.glob + | Erlang.value "reverse_dns_lookups" Erlang.boolean + | Erlang.value "cluster_keepalive_interval" Erlang.integer + | Erlang.value "mnesia_table_loading_timeout" Erlang.integer (* View: rabbit The rabbit config *) diff --git a/lenses/reprepro_uploaders.aug b/lenses/reprepro_uploaders.aug index e905fb1..b8acef8 100644 --- a/lenses/reprepro_uploaders.aug +++ b/lenses/reprepro_uploaders.aug @@ -9,7 +9,6 @@ About: Reference About: License This file is licenced under the LGPL v2+, like the rest of Augeas. - About: Lens Usage See . @@ -59,6 +58,7 @@ let condition_re = | "sections" | "binaries" | "architectures" + | "distribution" (* View: condition_field A single condition field is an 'or' node. @@ -128,6 +128,16 @@ let by_key = . store (Rx.word - "any") ] in value "key" . (any_key | named_key) +(* View: by_group + Authenticate packages by a groupname. + + > $reprepro/allow[1]/by/group = "groupname" + + *) +let by_group = value "group" + . [ key "group" . Sep.space + . store Rx.word ] + (* View: by statements define who is allowed to upload. It can be simple keywords, like "anybody" or "unsigned", @@ -142,10 +152,10 @@ let by_key = let by = [ key "by" . Sep.space . ( store ("anybody"|"unsigned") - | by_key ) ] + | by_key | by_group ) ] -(* View: entry - An entry is an allow statement, e.g.: +(* View: allow + An allow entry, e.g.: > $reprepro/allow[1] > $reprepro/allow[1]/and[1] @@ -161,11 +171,30 @@ let by = > $reprepro/allow[1]/by/key = "ABCD1234" *) -let entry = +let allow = [ key "allow" . Sep.space . condition_list . Sep.space . by . Util.eol ] +(* View: group + A group declaration *) +let group = + let add = [ key "add" . Sep.space + . store Rx.word ] + in let contains = [ key "contains" . Sep.space + . store Rx.word ] + in let empty = [ key "empty" ] + in let unused = [ key "unused" ] + in [ key "group" . Sep.space + . store Rx.word . Sep.space + . (add | contains | empty | unused) . Util.eol ] + +(* View: entry + An entry is either an statement + or a definition. + *) +let entry = allow | group + (* View: lns The lens is made of , and lines *) let lns = (Util.empty|Util.comment|entry)* diff --git a/lenses/rhsm.aug b/lenses/rhsm.aug new file mode 100644 index 0000000..56cc82e --- /dev/null +++ b/lenses/rhsm.aug @@ -0,0 +1,42 @@ +(* +Module: Rhsm + Parses subscription-manager config files + +Author: Dominic Cleal + +About: Reference + This lens tries to keep as close as possible to rhsm.conf(5) and + Python's SafeConfigParser. All settings must be in sections without + indentation. Semicolons and hashes are permitted for comments. + +About: License + This file is licenced under the LGPL v2+, like the rest of Augeas. + +About: Lens Usage + To be documented + +About: Configuration files + This lens applies to: + /etc/rhsm/rhsm.conf + + See . +*) + +module Rhsm = + autoload xfm + +(* Semicolons and hashes are permitted for comments *) +let comment = IniFile.comment IniFile.comment_re "#" +(* Equals and colons are permitted for separators *) +let sep = IniFile.sep IniFile.sep_re IniFile.sep_default + +(* All settings must be in sections without indentation *) +let entry = IniFile.entry_multiline IniFile.entry_re sep comment +let title = IniFile.title IniFile.record_re +let record = IniFile.record title entry + +let lns = IniFile.lns record comment + +let filter = incl "/etc/rhsm/rhsm.conf" + +let xfm = transform lns filter diff --git a/lenses/rsyslog.aug b/lenses/rsyslog.aug index 943847f..7fc78b4 100644 --- a/lenses/rsyslog.aug +++ b/lenses/rsyslog.aug @@ -37,7 +37,9 @@ let omusrmsg = Util.del_str ":omusrmsg:" . File action with a specified template *) let file_tmpl = Syslog.file . [ label "template" . Util.del_str ";" . store Rx.word ] -let action = Syslog.action | omusrmsg | file_tmpl +let namedpipe = Syslog.pipe . Sep.space . [ label "pipe" . store Syslog.file_r ] + +let action = Syslog.action | omusrmsg | file_tmpl | namedpipe (* View: entry An entry contains selectors and an action @@ -48,7 +50,7 @@ let entry = [ label "entry" . Syslog.selectors . Syslog.sep_tab . (* View: prop_filter Parses property-based filters, which start with ":" and the property name *) let prop_filter = - let sep = Sep.comma . Util.del_ws_spc + let sep = Sep.comma . Util.del_opt_ws " " in let prop_name = [ Util.del_str ":" . label "property" . store Rx.word ] in let prop_oper = [ label "operation" . store /[A-Za-z!-]+/ ] in let prop_val = [ label "value" . Quote.do_dquote (store /[^\n"]*/) ] diff --git a/lenses/rx.aug b/lenses/rx.aug index 5fb0851..05fbbe9 100644 --- a/lenses/rx.aug +++ b/lenses/rx.aug @@ -35,7 +35,7 @@ let cl_or_opt_space = cl | opt_space (* Group: General strings *) (* Variable: space_in - A string which not starting or ending with a space *) + A string which does not start or end with a space *) let space_in = /[^ \r\t\n].*[^ \r\t\n]|[^ \t\n\r]/ (* Variable: no_spaces diff --git a/lenses/shellvars.aug b/lenses/shellvars.aug index 25bb82b..7da7779 100644 --- a/lenses/shellvars.aug +++ b/lenses/shellvars.aug @@ -20,6 +20,7 @@ module Shellvars = let empty_part_re = Util.empty_generic_re . /\n+/ let eol = del (/[ \t]+|[ \t]*[;\n]/ . empty_part_re*) "\n" let semicol_eol = del (/[ \t]*[;\n]/ . empty_part_re*) "\n" + let brace_eol = del /[ \t\n]+/ "\n" let key_re = /[A-Za-z0-9_]+(\[[0-9A-Za-z_,]+\])?/ - ("unset" | "export") let matching_re = "${!" . key_re . /[\*@]\}/ @@ -34,33 +35,25 @@ module Shellvars = let xchgs = Build.xchgs let semicol = del /;?/ "" - let char = /[^`;() '"\t\n]|\\\\"/ + let char = /[^`;()'"&|\n\\# \t]#*|\\\\./ let dquot = let char = /[^"\\]|\\\\./ | Rx.cl in "\"" . char* . "\"" (* " Emacs, relax *) let squot = /'[^']*'/ - let bquot = /`[^`\n]*`/ + let bquot = /`[^`\n]+`/ (* dbquot don't take spaces or semi-colons *) let dbquot = /``[^` \t\n;]+``/ let dollar_assign = /\$\([^\(\)#\n]*\)/ let dollar_arithm = /\$\(\([^\)#\n]*\)\)/ - let anyquot = (dquot|squot)+ | bquot | dbquot | dollar_assign | dollar_arithm - - let to_semicol_re = /[^#; \t\n][^#;\n]+[^#; \t\n]|[^#; \t\n]+/ - let sto_to_semicol = store to_semicol_re - - let sto_to_semicol_quot = - let no_semicol_re = /[^"'#;\n]/ - in let no_semicol_spc_re = /[^"'#; \t\n]/ - in let multi_chars = no_semicol_spc_re . (no_semicol_re|anyquot)+ . no_semicol_spc_re - in store (no_semicol_spc_re | multi_chars) + let anyquot = (char|dquot|squot|dollar_assign|dollar_arithm)+ | bquot | dbquot + let sto_to_semicol = store (anyquot . (Rx.cl_or_space . anyquot)*) (* Array values of the form '(val1 val2 val3)'. We do not handle empty *) (* arrays here because of typechecking headaches. Instead, they are *) (* treated as a simple value *) let array = - let array_value = store (char+ | anyquot) in + let array_value = store anyquot in del /\([ \t]*/ "(" . counter "values" . [ seq "values" . array_value ] . [ del /[ \t\n]+/ " " . seq "values" . array_value ] * @@ -70,7 +63,7 @@ module Shellvars = (* but fairly close. *) let simple_value = let empty_array = /\([ \t]*\)/ in - store (char* | anyquot | empty_array) + store (anyquot | empty_array)? let export = [ key "export" . Util.del_ws_spc ] let kv = Util.indent . export? . key key_re @@ -91,10 +84,19 @@ module Shellvars = let shell_builtin_cmds = "ulimit" | "shift" | "exit" + let eval = + Util.indent . Util.del_str "eval" . Util.del_ws_spc + . label "@eval" . store anyquot + + let alias = + Util.indent . Util.del_str "alias" . Util.del_ws_spc + . label "@alias" . store key_re . eq + . [ label "value" . store anyquot ] + let builtin = Util.indent . label "@builtin" . store shell_builtin_cmds - . (Util.del_ws_spc + . (Sep.cl_or_space . [ label "args" . sto_to_semicol ])? let keyword (kw:string) = Util.indent . Util.del_str kw @@ -105,6 +107,68 @@ module Shellvars = . Util.del_str "return" . ( Util.del_ws_spc . store Rx.integer )? + let action (operator:string) (lbl:string) (sto:lens) = + let sp = Rx.cl_or_opt_space | /[ \t\n]+/ + in [ del (sp . operator . sp) (" " . operator . " ") + . label ("@".lbl) . sto ] + + let action_pipe = action "|" "pipe" + let action_and = action "&&" "and" + let action_or = action "||" "or" + + let condition = + let cond (start:string) (end:string) = [ label "type" . store start ] + . Util.del_ws_spc . sto_to_semicol + . Util.del_ws_spc . Util.del_str end + . ( action_and sto_to_semicol | action_or sto_to_semicol )* + in Util.indent . label "@condition" . (cond "[" "]" | cond "[[" "]]") + + (* Entry types *) + let entry_eol_item (item:lens) = [ item . comment_or_eol ] + let entry_item (item:lens) = [ item ] + + let entry_eol_nocommand = + entry_eol_item source + | entry_eol_item kv + | entry_eol_item unset + | entry_eol_item bare_export + | entry_eol_item builtin + | entry_eol_item return + | entry_eol_item condition + | entry_eol_item eval + | entry_eol_item alias + + let entry_noeol_nocommand = + entry_item source + | entry_item kv + | entry_item unset + | entry_item bare_export + | entry_item builtin + | entry_item return + | entry_item condition + | entry_item eval + | entry_item alias + + (* Command *) + let rec command = + let env = [ key key_re . eq . store anyquot . Sep.cl_or_space ] + in let reserved_key = /exit|shift|return|ulimit|unset|export|source|\.|if|for|select|while|until|then|else|fi|done|case|eval|alias/ + in let word = /[A-Za-z0-9_.-\/]+/ + in let entry_eol = entry_eol_nocommand | entry_eol_item command + in let entry_noeol = entry_noeol_nocommand | entry_item command + in let entry = entry_eol | entry_noeol + in let pipe = action_pipe (entry_eol_item command | entry_item command) + in let and = action_and entry + in let or = action_or entry + in Util.indent . label "@command" . env* . store (word - reserved_key) + . [ Sep.cl_or_space . label "@arg" . sto_to_semicol]? + . ( pipe | and | or )? + + let entry_eol = entry_eol_nocommand + | entry_eol_item command + + let entry_noeol = entry_noeol_nocommand + | entry_item command (************************************************************************ * Group: CONDITIONALS AND LOOPS @@ -113,7 +177,7 @@ module Shellvars = let generic_cond_start (start_kw:string) (lbl:string) (then_kw:string) (contents:lens) = keyword_label start_kw lbl . Sep.space - . sto_to_semicol_quot . semicol_eol + . sto_to_semicol . semicol_eol . keyword then_kw . eol . contents @@ -140,8 +204,10 @@ module Shellvars = generic_cond "select" "@select" "do" entry+ "done" let case (entry:lens) (entry_noeol:lens) = - let case_entry = [ label "@case_entry" - . Util.indent . store /[^ \t\n\)]+/ + let pattern = [ label "@pattern" . sto_to_semicol . Sep.opt_space ] + in let case_entry = [ label "@case_entry" + . Util.indent . pattern + . (Util.del_str "|" . Sep.opt_space . pattern)* . Util.del_str ")" . eol . entry* . entry_noeol? . Util.indent . Util.del_str ";;" . eol ] in @@ -152,32 +218,19 @@ module Shellvars = . empty* . comment* . keyword "esac" . comment_or_eol ] + let subshell (entry:lens) = + [ Util.indent . label "@subshell" + . Util.del_str "{" . brace_eol + . entry+ + . Util.indent . Util.del_str "}" . eol ] + let function (entry:lens) = [ Util.indent . label "@function" - . del /(function[ \t]+)?/ "" - . store Rx.word . del /[ \t]*\(\)/ "()" - . eol . Util.del_str "{" . eol - . entry+ - . Util.indent . Util.del_str "}" . eol ] - - let entry_eol = - let entry_eol_item (item:lens) = - [ item . comment_or_eol ] in - entry_eol_item source - | entry_eol_item kv - | entry_eol_item unset - | entry_eol_item bare_export - | entry_eol_item builtin - | entry_eol_item return - - let entry_noeol = - let entry_item (item:lens) = [ item ] in - entry_item source - | entry_item kv - | entry_item unset - | entry_item bare_export - | entry_item builtin - | entry_item return + . del /(function[ \t]+)?/ "" + . store Rx.word . del /[ \t]*\(\)/ "()" + . (comment_eol|brace_eol) . Util.del_str "{" . brace_eol + . entry+ + . Util.indent . Util.del_str "}" . eol ] let rec rec_entry = let entry = comment | entry_eol | rec_entry in @@ -188,6 +241,7 @@ module Shellvars = | loop_until entry | case entry entry_noeol | function entry + | subshell entry let lns_norec = del_empty* . (comment | entry_eol) * @@ -216,6 +270,7 @@ module Shellvars = sc_incl "network/if-down.d/*" . sc_incl "network/ifroute-*" . sc_incl "network/if-up.d/*" . + sc_excl "network/if-up.d/SuSEfirewall2" . sc_incl "network/providers/*" . sc_excl "network-scripts" . sc_incl "network-scripts/ifcfg-*" . @@ -233,8 +288,10 @@ module Shellvars = let filter_default = incl "/etc/default/*" . excl "/etc/default/grub_installdevice*" . excl "/etc/default/rmt" + . excl "/etc/default/star" . excl "/etc/default/whoopsie" let filter_misc = incl "/etc/arno-iptables-firewall/debconf.cfg" + . incl "/etc/conf.d/*" . incl "/etc/cron-apt/config" . incl "/etc/environment" . incl "/etc/firewalld/firewalld.conf" @@ -244,6 +301,7 @@ module Shellvars = . incl "/etc/cvs-cron.conf" . incl "/etc/cvs-pserver.conf" . incl "/etc/devscripts.conf" + . incl "/etc/kamailio/kamctlrc" . incl "/etc/lintianrc" . incl "/etc/lsb-release" . incl "/etc/os-release" diff --git a/lenses/simplelines.aug b/lenses/simplelines.aug index 38f2189..8ff25fb 100644 --- a/lenses/simplelines.aug +++ b/lenses/simplelines.aug @@ -43,5 +43,6 @@ let filter = incl "/etc/at.allow" . incl "/etc/cron.d/cron.deny" . incl "/etc/default/grub_installdevice" . incl "/etc/pam.d/allow.pamlist" + . incl "/etc/hostname.*" let xfm = transform lns filter diff --git a/lenses/smbusers.aug b/lenses/smbusers.aug index c9aed37..aa70dc1 100644 --- a/lenses/smbusers.aug +++ b/lenses/smbusers.aug @@ -25,7 +25,7 @@ let entry = in Build.key_value_line Rx.word Sep.space_equal usernames (* View: lns *) -let lns = (Util.empty | Util.comment | entry)* +let lns = (Util.empty | (Util.comment_generic /[ \t]*[#;][ \t]*/ "# ") | entry)* (* Variable: filter *) let filter = incl "/etc/samba/smbusers" diff --git a/lenses/spacevars.aug b/lenses/spacevars.aug index 081251d..9f70b06 100644 --- a/lenses/spacevars.aug +++ b/lenses/spacevars.aug @@ -24,11 +24,13 @@ let empty = Util.empty let entry = Build.key_ws_value /[A-Za-z0-9._-]+(\[[0-9]+\])?/ +let flag = [ key /[A-Za-z0-9._-]+(\[[0-9]+\])?/ . Util.doseol ] + (************************************************************************ * LENS *************************************************************************) -let lns = (comment|empty|entry)* +let lns = (comment|empty|entry|flag)* let simple_lns = lns (* An alias for compatibility reasons *) diff --git a/lenses/ssh.aug b/lenses/ssh.aug index d686594..2e9c43d 100644 --- a/lenses/ssh.aug +++ b/lenses/ssh.aug @@ -54,6 +54,11 @@ module Ssh = in [ indent . key k . counter "commas_entry" . spc . Build.opt_list value comma . eol ] + let spaces_entry (k:regexp) = + let value = [ seq "spaces_entry" . value_to_spc ] + in [ indent . key k . counter "spaces_entry" . spc . + Build.opt_list value spc . eol ] + let fw_entry (k:regexp) = [ indent . key k . spc . [ key /[^ \t\r\n\/]+/ . spc . value_to_eol . eol ]] @@ -66,6 +71,10 @@ module Ssh = let ciphers = commas_entry /Ciphers/i let macs = commas_entry /MACs/i + let algorithms = commas_entry /(HostKey|Kex)Algorithms/i + let pubkey_accepted_key_types = commas_entry /PubkeyAcceptedKeyTypes/i + + let global_knownhosts_file = spaces_entry /GlobalKnownHostsFile/i let special_entry = send_env | proxy_command @@ -73,9 +82,12 @@ module Ssh = | local_fw | macs | ciphers + | algorithms + | pubkey_accepted_key_types + | global_knownhosts_file let key_re = /[A-Za-z0-9]+/ - - /SendEnv|Host|ProxyCommand|RemoteForward|LocalForward|MACs|Ciphers/i + - /SendEnv|Host|ProxyCommand|RemoteForward|LocalForward|MACs|Ciphers|(HostKey|Kex)Algorithms|PubkeyAcceptedKeyTypes|GlobalKnownHostsFile/i let other_entry = [ indent . key key_re diff --git a/lenses/star.aug b/lenses/star.aug new file mode 100644 index 0000000..b8c0a44 --- /dev/null +++ b/lenses/star.aug @@ -0,0 +1,31 @@ +(* +Module: Star + Parses star's configuration file + +Author: Cedric Bosdonnat + +About: Reference + This lens is based on star(1) + +About: License + This file is licensed under the LGPL v2+, like the rest of Augeas. +*) + +module Star = + autoload xfm + +let sto_to_tab = store Rx.no_spaces + +let size = Build.key_value_line "STAR_FIFOSIZE" Sep.space_equal ( store /[0-9x*.a-z]+/ ) +let size_max = Build.key_value_line "STAR_FIFOSIZE_MAX" Sep.space_equal ( store /[0-9x*.a-z]+/ ) +let archive = Build.key_value_line ( "archive". /[0-7]/ ) Sep.equal + ( [ label "device" . sto_to_tab ] . Sep.tab . + [ label "block" . sto_to_tab ] . Sep.tab . + [ label "size" . sto_to_tab ] . ( Sep.tab . + [ label "istape" . sto_to_tab ] )? ) + +let lns = ( size | size_max | archive | Util.comment | Util.empty )* + +let filter = incl "/etc/default/star" + +let xfm = transform lns filter diff --git a/lenses/sudoers.aug b/lenses/sudoers.aug index 7567772..2a63d9e 100644 --- a/lenses/sudoers.aug +++ b/lenses/sudoers.aug @@ -82,15 +82,37 @@ let sep_col = sep_cont_opt_build ":" (* Variable: sep_dquote *) let sep_dquote = Util.del_str "\"" +(* Group: Negation expressions *) + +(************************************************************************ + * View: del_negate + * Delete an even number of '!' signs + *************************************************************************) +let del_negate = del /(!!)*/ "" + +(************************************************************************ + * View: negate_node + * Negation of boolean values for . Accept one optional '!' + * and produce a 'negate' node if there is one. + *************************************************************************) +let negate_node = [ del "!" "!" . label "negate" ] + +(************************************************************************ + * View: negate_or_value + * A , followed by either a negated key, or a key/value pair + *************************************************************************) +let negate_or_value (key:lens) (value:lens) = + [ del_negate . (negate_node . key | key . value) ] (* Group: Stores *) (* Variable: sto_to_com_cmnd sto_to_com_cmnd does not begin or end with a space *) -let sto_to_com_cmnd = + +let sto_to_com_cmnd = del_negate . negate_node? . ( let alias = Rx.word - /(NO)?(PASSWD|EXEC|SETENV)/ - in let non_alias = /(!?[\/a-z]([^,:#()\n\\]|\\\\[=:,\\])*[^,=:#() \t\n\\])|[^,=:#() \t\n\\]/ - in store (alias | non_alias) + in let non_alias = /[\/a-z]([^,:#()\n\\]|\\\\[=:,\\])*[^,=:#() \t\n\\]|[^,=:#() \t\n\\]/ + in store (alias | non_alias)) (* Variable: sto_to_com @@ -251,10 +273,12 @@ let cmnd_alias = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd * > 'Cmnd_Alias' Cmnd_Alias (':' Cmnd_Alias)* *************************************************************************) let alias = user_alias | runas_alias | host_alias | cmnd_alias + (************************************************************************ * Group: DEFAULTS *************************************************************************) + (************************************************************************ * View: default_type * Type definition for @@ -271,22 +295,6 @@ let default_type = [ label "type" . value ] (************************************************************************ - * View: del_negate - * Delete an even number of '!' signs - *************************************************************************) -let del_negate = del /(!!)*/ "" - -(************************************************************************ - * View: negate_node - * Negation of boolean values for . Accept one optional '!' - * and produce a 'negate' node if there is one. - *************************************************************************) -let negate_node = [ del "!" "!" . label "negate" ] - -let negate_or_value (key:lens) (value:lens) = - [ del_negate . (negate_node . key | key . value) ] - -(************************************************************************ * View: parameter_flag * A flag parameter for * diff --git a/lenses/syslog.aug b/lenses/syslog.aug index f8b99c4..7824f7d 100644 --- a/lenses/syslog.aug +++ b/lenses/syslog.aug @@ -198,10 +198,15 @@ module Syslog = *) let logprogram = pipe . [ label "program" . store /[^ \t\n][^\n]+[^ \t\n]/ ] + (* View: discard + discards matching messages + *) + let discard = [ label "discard" . Util.del_str "~" ] + (* View: action - an action is either a file, a host, users, or a program + an action is either a file, a host, users, a program, or discard *) - let action = (file | loghost | users | logprogram) + let action = (file | loghost | users | logprogram | discard) (* Group: Entry *) diff --git a/lenses/tests/test_aptconf.aug b/lenses/tests/test_aptconf.aug index c835b10..6c11ace 100644 --- a/lenses/tests/test_aptconf.aug +++ b/lenses/tests/test_aptconf.aug @@ -172,3 +172,10 @@ Unattended-Upgrade::Package-Blacklist { test AptConf.lns get complex_elem = { "DPkg" { "Post-Invoke" { "@elem" = "if [ -d /var/lib/update-notifier ]; then touch /var/lib/update-notifier/dpkg-run-stamp; fi; if [ -e /var/lib/update-notifier/updates-available ]; then echo > /var/lib/update-notifier/updates-available; fi " } } } + + (* Accept hash comments *) + test AptConf.lns get "# a comment\n" = + { "#comment" = "a comment" } + + (* Accept empty hash comments *) + test AptConf.lns get "# \n" = { } diff --git a/lenses/tests/test_aptsource.aug b/lenses/tests/test_aptsources.aug similarity index 66% rename from lenses/tests/test_aptsource.aug rename to lenses/tests/test_aptsources.aug index 181e0f3..7546393 100644 --- a/lenses/tests/test_aptsource.aug +++ b/lenses/tests/test_aptsources.aug @@ -1,4 +1,4 @@ -module Test_aptsource = +module Test_aptsources = let simple_source = "deb ftp://mirror.bytemark.co.uk/debian/ etch main\n" let multi_components = "deb http://security.debian.org/ etch/updates main contrib non-free\n" @@ -65,6 +65,38 @@ deb ftp://mirror.bytemark.co.uk/debian/ etch main non-free contrib set "/1/type" "deb" = trailing_comment + (* Support options, GH #295 *) + test Aptsources.lns get "deb [arch=amd64] tor+http://ftp.us.debian.org/debian sid main contrib +deb [ arch+=amd64 trusted-=true ] http://ftp.us.debian.org/debian sid main contrib\n" = + { "1" + { "type" = "deb" } + { "options" + { "arch" = "amd64" } + } + { "uri" = "tor+http://ftp.us.debian.org/debian" } + { "distribution" = "sid" } + { "component" = "main" } + { "component" = "contrib" } } + { "2" + { "type" = "deb" } + { "options" + { "arch" = "amd64" { "operation" = "+" } } + { "trusted" = "true" { "operation" = "-" } } + } + { "uri" = "http://ftp.us.debian.org/debian" } + { "distribution" = "sid" } + { "component" = "main" } + { "component" = "contrib" } } + + (* cdrom entries may have spaces, GH #296 *) + test Aptsources.lns get "deb cdrom:[Debian GNU/Linux 7.5.0 _Wheezy_ - Official amd64 CD Binary-1 20140426-13:37]/ wheezy main\n" = + { "1" + { "type" = "deb" } + { "uri" = "cdrom:[Debian GNU/Linux 7.5.0 _Wheezy_ - Official amd64 CD Binary-1 20140426-13:37]/" } + { "distribution" = "wheezy" } + { "component" = "main" } } + + (* Local Variables: *) (* mode: caml *) (* End: *) diff --git a/lenses/tests/test_chrony.aug b/lenses/tests/test_chrony.aug index c4b552e..05ffccc 100644 --- a/lenses/tests/test_chrony.aug +++ b/lenses/tests/test_chrony.aug @@ -20,18 +20,27 @@ server ntp3.example.com presend 2 server ntp4.example.com offline polltarget 4 server ntp5.example.com maxdelay 2 offline server ntp6.example.com maxdelay 2 iburst presend 2 -server ntp7.example.com iburst presend 2 offline +server ntp7.example.com iburst presend 2 offline prefer trust require +server ntp8.example.com minsamples 8 maxsamples 16 version 3 peer ntpc1.example.com +pool pool1.example.com iburst maxsources 3 +allow +deny all +cmdallow 192.168.1.0/24 +cmddeny all 192.168.2.0/24 stratumweight 0 -driftfile /var/lib/chrony/drift -rtcsync -makestep 10 3 + driftfile /var/lib/chrony/drift + rtcsync +makestep 10 -1 bindcmdaddress 127.0.0.1 bindcmdaddress ::1 +local local stratum 10 +local distance 1.0 orphan keyfile /etc/chrony.keys commandkey 1 generatecommandkey +manual noclientlog logchange 0.5 logdir /var/log/chrony @@ -42,8 +51,17 @@ broadcast 10 192.168.100.255 123 fallbackdrift 16 19 mailonchange root@localhost 0.5 maxchange 1000 1 2 +maxdistance 1.0 +maxdrift 100 initstepslew 30 foo.bar.com initstepslew 30 foo.bar.com baz.quz.com +ratelimit interval 4 burst 16 leak 2 +cmdratelimit +refclock SHM 0 refid SHM0 delay 0.1 offset 0.2 noselect +refclock PPS /dev/pps0 dpoll 2 poll 3 lock SHM0 rate 5 minsamples 8 +smoothtime 400 0.001 leaponly +tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 26000 0.0 0.000183 0.0 +tempcomp /sys/class/hwmon/hwmon0/temp2_input 30 /etc/chrony.tempcomp " test Chrony.lns get exampleconf = @@ -80,23 +98,49 @@ initstepslew 30 foo.bar.com baz.quz.com { "iburst" } { "presend" = "2" } { "offline" } + { "prefer" } + { "trust" } + { "require" } + } + { "server" = "ntp8.example.com" + { "minsamples" = "8" } + { "maxsamples" = "16" } + { "version" = "3" } } { "peer" = "ntpc1.example.com" } + { "pool" = "pool1.example.com" + { "iburst" } + { "maxsources" = "3" } + } + { "allow" } + { "deny" + { "all" } + } + { "cmdallow" = "192.168.1.0/24" } + { "cmddeny" = "192.168.2.0/24" + { "all" } + } { "stratumweight" = "0" } { "driftfile" = "/var/lib/chrony/drift" } { "rtcsync" } { "makestep" { "threshold" = "10" } - { "limit" = "3" } + { "limit" = "-1" } } { "bindcmdaddress" = "127.0.0.1" } { "bindcmdaddress" = "::1" } + { "local" } { "local" { "stratum" = "10" } } + { "local" + { "distance" = "1.0" } + { "orphan" } + } { "keyfile" = "/etc/chrony.keys" } { "commandkey" = "1" } { "generatecommandkey" } + { "manual" } { "noclientlog" } { "logchange" = "0.5" } { "logdir" = "/var/log/chrony" } @@ -114,7 +158,6 @@ initstepslew 30 foo.bar.com baz.quz.com { "address" = "192.168.100.255" } { "port" = "123" } } - { } { "fallbackdrift" { "min" = "16" } { "max" = "19" } @@ -128,6 +171,8 @@ initstepslew 30 foo.bar.com baz.quz.com { "delay" = "1" } { "limit" = "2" } } + { "maxdistance" = "1.0" } + { "maxdrift" = "100" } { "initstepslew" { "threshold" = "30" } { "address" = "foo.bar.com" } @@ -137,6 +182,47 @@ initstepslew 30 foo.bar.com baz.quz.com { "address" = "foo.bar.com" } { "address" = "baz.quz.com" } } + { "ratelimit" + { "interval" = "4" } + { "burst" = "16" } + { "leak" = "2" } + } + { "cmdratelimit" } + { "refclock" + { "driver" = "SHM" } + { "parameter" = "0" } + { "refid" = "SHM0" } + { "delay" = "0.1" } + { "offset" = "0.2" } + { "noselect" } + } + { "refclock" + { "driver" = "PPS" } + { "parameter" = "/dev/pps0" } + { "dpoll" = "2" } + { "poll" = "3" } + { "lock" = "SHM0" } + { "rate" = "5" } + { "minsamples" = "8" } + } + { "smoothtime" + { "maxfreq" = "400" } + { "maxwander" = "0.001" } + { "leaponly" } + } + { "tempcomp" + { "sensorfile" = "/sys/class/hwmon/hwmon0/temp2_input" } + { "interval" = "30" } + { "t0" = "26000" } + { "k0" = "0.0" } + { "k1" = "0.000183" } + { "k2" = "0.0" } + } + { "tempcomp" + { "sensorfile" = "/sys/class/hwmon/hwmon0/temp2_input" } + { "interval" = "30" } + { "pointfile" = "/etc/chrony.tempcomp" } + } (* Local Variables: *) diff --git a/lenses/tests/test_csv.aug b/lenses/tests/test_csv.aug new file mode 100644 index 0000000..b24f447 --- /dev/null +++ b/lenses/tests/test_csv.aug @@ -0,0 +1,91 @@ +module Test_CSV = + +(* Test: CSV.lns + Simple test *) +test CSV.lns get "a,b,c\n" = + { "1" + { "1" = "a" } + { "2" = "b" } + { "3" = "c" } } + +(* Test: CSV.lns + Values with spaces *) +test CSV.lns get "a,b c,d\n" = + { "1" + { "1" = "a" } + { "2" = "b c" } + { "3" = "d" } } + +(* Test: CSV.lns + Quoted values *) +test CSV.lns get "a,\"b,c\",d +# comment +# +e,f,with space\n" = + { "1" + { "1" = "a" } + { "2" = "\"b,c\"" } + { "3" = "d" } } + { "#comment" = "comment" } + { } + { "2" + { "1" = "e" } + { "2" = "f" } + { "3" = "with space" } } + +(* Test: CSV.lns + Empty values *) +test CSV.lns get ", +,,\n" = + { "1" + { "1" = "" } + { "2" = "" } } + { "2" + { "1" = "" } + { "2" = "" } + { "3" = "" } } + +(* Test: CSV.lns + Trailing spaces *) +test CSV.lns get "a , b + \n" = + { "1" + { "1" = "a " } + { "2" = " b " } } + { "2" + { "1" = " " } } + +(* Test: CSV.lns + Quoted values in quoted values *) +test CSV.lns get "\"a,b\"\"c d\"\"\"\n" = + { "1" { "1" = "\"a,b\"\"c d\"\"\"" } } + +(* Test: CSV.lns + Quote in quoted values *) +test CSV.lns get "\"a,b\"\"c d\"\n" = + { "1" { "1" = "\"a,b\"\"c d\"" } } + +(* Test: CSV.lns + Values with newlines *) +test CSV.lns get "a,\"b\n c\"\n" = + { "1" + { "1" = "a" } + { "2" = "\"b\n c\"" } } + +(* Test: CSV.lns_semicol + Semi-colon lens *) +test CSV.lns_semicol get "a;\"b;c\";d +# comment +# +e;f;with space\n" = + { "1" + { "1" = "a" } + { "2" = "\"b;c\"" } + { "3" = "d" } } + { "#comment" = "comment" } + { } + { "2" + { "1" = "e" } + { "2" = "f" } + { "3" = "with space" } } + diff --git a/lenses/tests/test_cups.aug b/lenses/tests/test_cups.aug index 62c5460..276c5d1 100644 --- a/lenses/tests/test_cups.aug +++ b/lenses/tests/test_cups.aug @@ -199,7 +199,6 @@ test Cups.lns get conf = { "arg" = "allow,deny" } } } - { } { "#comment" = "Restrict access to the admin pages..." } { "Location" { "arg" = "/admin" } @@ -207,7 +206,6 @@ test Cups.lns get conf = { "arg" = "allow,deny" } } } - { } { "#comment" = "Restrict access to configuration files..." } { "Location" { "arg" = "/admin/conf" } @@ -222,7 +220,6 @@ test Cups.lns get conf = { "arg" = "allow,deny" } } } - { } { "#comment" = "Set the default printer/job policies..." } { "Policy" { "arg" = "default" } @@ -250,7 +247,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "Limit" { "arg" = "Send-Document" } { "arg" = "Send-URI" } @@ -280,7 +276,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "All administration operations require an administrator to authenticate..." } { "Limit" { "arg" = "CUPS-Add-Modify-Printer" } @@ -300,7 +295,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "All printer operations require a printer operator to authenticate..." } { "Limit" { "arg" = "Pause-Printer" } @@ -331,7 +325,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "Only the owner or an administrator can cancel or authenticate a job..." } { "Limit" { "arg" = "Cancel-Job" } @@ -345,7 +338,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "Limit" { "arg" = "All" } { "directive" = "Order" @@ -353,7 +345,6 @@ test Cups.lns get conf = } } } - { } { "#comment" = "Set the authenticated printer/job policies..." } { "Policy" { "arg" = "authenticated" } @@ -384,7 +375,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "Limit" { "arg" = "Send-Document" } { "arg" = "Send-URI" } @@ -417,7 +407,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "All administration operations require an administrator to authenticate..." } { "Limit" { "arg" = "CUPS-Add-Modify-Printer" } @@ -436,7 +425,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "All printer operations require a printer operator to authenticate..." } { "Limit" { "arg" = "Pause-Printer" } @@ -467,7 +455,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "#comment" = "Only the owner or an administrator can cancel or authenticate a job..." } { "Limit" { "arg" = "Cancel-Job" } @@ -484,7 +471,6 @@ test Cups.lns get conf = { "arg" = "deny,allow" } } } - { } { "Limit" { "arg" = "All" } { "directive" = "Order" diff --git a/lenses/tests/test_dhclient.aug b/lenses/tests/test_dhclient.aug index 791b5ec..6bc1d75 100644 --- a/lenses/tests/test_dhclient.aug +++ b/lenses/tests/test_dhclient.aug @@ -148,3 +148,6 @@ test Dhclient.lns get "send dhcp-client-identifier = hardware;\n" = { "send" { "dhcp-client-identifier" { "#eval" = "hardware" } } } + +test Dhclient.lns put "send host-name = gethostname();\n" + after set "/send/host-name/#eval" "test" = "send host-name = test;\n" diff --git a/lenses/tests/test_erlang.aug b/lenses/tests/test_erlang.aug index 44bb095..89ca61d 100644 --- a/lenses/tests/test_erlang.aug +++ b/lenses/tests/test_erlang.aug @@ -49,6 +49,14 @@ test tuple_bare get "{foo, bar}" = { "value" = "foo" } { "value" = "bar" } } +let tuple3_bare = Erlang.tuple3 Erlang.bare Erlang.bare Erlang.bare + +test tuple3_bare get "{foo, bar, baz}" = + { "tuple" + { "value" = "foo" } + { "value" = "bar" } + { "value" = "baz" } } + (* Group: application *) let list_bare_app = Erlang.application (Rx.word - "kernel") list_bare diff --git a/lenses/tests/test_group.aug b/lenses/tests/test_group.aug index 6d657b5..37c27fa 100644 --- a/lenses/tests/test_group.aug +++ b/lenses/tests/test_group.aug @@ -47,3 +47,8 @@ test Group.lns get "+:::\n" = { "@nisdefault" { "password" = "" } { "gid" = "" } } + +test Group.lns get "+:*::\n" = + { "@nisdefault" + { "password" = "*" } + { "gid" = "" } } diff --git a/lenses/tests/test_host_conf.aug b/lenses/tests/test_host_conf.aug index 146abde..f0df5ab 100644 --- a/lenses/tests/test_host_conf.aug +++ b/lenses/tests/test_host_conf.aug @@ -3,7 +3,7 @@ module Test_Host_Conf = let conf = " # /etc/host.conf # We have named running, but no NIS (yet) -order bind,hosts +order bind, hosts # Allow multiple addrs multi on # Guard against spoof attempts diff --git a/lenses/tests/test_httpd.aug b/lenses/tests/test_httpd.aug index f63a692..7d5e045 100644 --- a/lenses/tests/test_httpd.aug +++ b/lenses/tests/test_httpd.aug @@ -274,7 +274,6 @@ test Httpd.lns get conf2 = { "arg" = "all" } } } - { } { "directive" = "ScriptAlias" { "arg" = "/cgi-bin/" } { "arg" = "/usr/lib/cgi-bin/" } @@ -297,7 +296,6 @@ test Httpd.lns get conf2 = { "arg" = "all" } } } - { } { "directive" = "ErrorLog" { "arg" = "/var/log/apache2/error.log" } } @@ -342,7 +340,6 @@ test Httpd.lns get conf2 = { "arg" = "::1/128" } } } - { } } (* Eol comment *) @@ -398,7 +395,6 @@ test Httpd.lns get versioncheck = } } } - {} { "IfVersion" { "arg" = ">=" } { "arg" = "2.4" } @@ -466,3 +462,124 @@ test Httpd.lns get " { "#comment" = "one comment" } { "#comment" = "another comment" } } + +(* Issue #307: backslashes in regexes *) +test Httpd.lns get " + RewriteRule ^/(.*) http\:\/\/example\.com\/$1 [L,R,NE] + RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1] +\n" = + { "VirtualHost" + { "arg" = "*:80" } + { "directive" = "RewriteRule" + { "arg" = "^/(.*)" } + { "arg" = "http\:\/\/example\.com\/$1" } + { "arg" = "[L,R,NE]" } } + { "directive" = "RewriteRule" + { "arg" = "\.css\.gz$" } + { "arg" = "-" } + { "arg" = "[T=text/css,E=no-gzip:1]" } } } + +(* https://github.com/letsencrypt/letsencrypt/issues/1294#issuecomment-161805063 *) +test Httpd.lns get " +\n" = + { "IfModule" } + +(* https://github.com/letsencrypt/letsencrypt/issues/1693 *) +test Httpd.lns get " + + ServerAdmin admin@example.com + \n" = + { "IfModule" + { "arg" = "mod_ssl.c" } + { "VirtualHost" + { "arg" = "*:443" } + { "directive" = "ServerAdmin" + { "arg" = "admin@example.com" } } } } + +(* Double quotes inside braces in directive arguments + https://github.com/letsencrypt/letsencrypt/issues/1766 *) +test Httpd.lns get "SSLRequire %{SSL_CLIENT_S_DN_CN} in {\"foo@bar.com\", bar@foo.com}\n" = + { "directive" = "SSLRequire" + { "arg" = "%{SSL_CLIENT_S_DN_CN}" } + { "arg" = "in" } + { "wordlist" + { "arg" = "\"foo@bar.com\"" } + { "arg" = "bar@foo.com" } } } + +(* Issue #330: optional end double quote to directive arg, for messages *) +test Httpd.lns get "SSLCipherSuite \"EECDH+ECDSA+AESGCM EECDH+aRS$\n" = + { "directive" = "SSLCipherSuite" + { "arg" = "\"EECDH+ECDSA+AESGCM EECDH+aRS$" } } + +test Httpd.lns get "ErrorDocument 404 \"The requested file favicon.ico was not found.\n" = + { "directive" = "ErrorDocument" + { "arg" = "404" } + { "arg" = "\"The requested file favicon.ico was not found." } } + +(* Quotes inside a unquoted directive argument + https://github.com/letsencrypt/letsencrypt/issues/1934 *) +test Httpd.lns get " + WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=_graphite group=_graphite +\n" = + { "VirtualHost" + { "arg" = "*:80" } + { "directive" = "WSGIDaemonProcess" + { "arg" = "_graphite" } + { "arg" = "processes=5" } + { "arg" = "threads=5" } + { "arg" = "display-name='%{GROUP}'" } + { "arg" = "inactivity-timeout=120" } + { "arg" = "user=_graphite" } + { "arg" = "group=_graphite" } } } + +(* Issue #327: perl blocks *) +test Httpd.lns get " + Apache::AuthDBI->setCacheTime(600); +\n" = + { "Perl" = "\n Apache::AuthDBI->setCacheTime(600);\n" } + +(* Line continuations inside VirtualHost blocks *) +test Httpd.lns get " +\n" = + { "VirtualHost" + { "arg" = "0.0.0.0:7080" } + { "arg" = "[00000:000:000:0000::2]:7080" } + { "arg" = "0.0.0.0:7080" } + { "arg" = "127.0.0.1:7080" } } + +(* Blank line continuations inside VirtualHost blocks *) +test Httpd.lns get " +\n" = + { "VirtualHost" + { "arg" = "0.0.0.0:7080" } + { "arg" = "0.0.0.0:7080" } } + +(* Non-continuation backslashes inside VirtualHost section headings *) +test Httpd.lns get " + ExpiresActive Off +\n" = + { "FilesMatch" + { "arg" = "\.php$" } + { "directive" = "ExpiresActive" + { "arg" = "Off" } } } + +(* Escaped spaces in directive and section arguments *) +test Httpd.lns get "RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.+/trackback/?\ HTTP/ [NC]\n" = + { "directive" = "RewriteCond" + { "arg" = "%{THE_REQUEST}" } + { "arg" = "^[A-Z]{3,9}\ /.+/trackback/?\ HTTP/" } + { "arg" = "[NC]" } } + +test Httpd.lns get "\n" = + { "FilesMatch" + { "arg" = "\ test\.php$" } } diff --git a/lenses/tests/test_inputrc.aug b/lenses/tests/test_inputrc.aug index e269fd5..1535697 100644 --- a/lenses/tests/test_inputrc.aug +++ b/lenses/tests/test_inputrc.aug @@ -61,6 +61,8 @@ $if term=rxvt \"\\e[8~\": end-of-line \"\\eOc\": forward-word \"\\eOd\": backward-word +$else +\"\\e[G\": \",\" $endif # for non RH/Debian xterm, can't hurt for RH/Debian xterm @@ -157,6 +159,11 @@ test Inputrc.lns get conf = { "entry" = "\\eOd" { "mapping" = "backward-word" } } + { "@else" + { "entry" = "\\e[G" + { "mapping" = "\",\"" } + } + } } { } { "#comment" = "for non RH/Debian xterm, can't hurt for RH/Debian xterm" } diff --git a/lenses/tests/test_interfaces.aug b/lenses/tests/test_interfaces.aug index 818322a..271d8d8 100644 --- a/lenses/tests/test_interfaces.aug +++ b/lenses/tests/test_interfaces.aug @@ -134,3 +134,8 @@ test Interfaces.lns put "" after test Interfaces.lns put "" after set "/source[0]" "/etc/network/conf.d/*.conf" = "source /etc/network/conf.d/*.conf\n" + +(* Test: Interfaces.lns + source-directory (Issue #306) *) +test Interfaces.lns get "source-directory interfaces.d\n" = + { "source-directory" = "interfaces.d" } diff --git a/lenses/tests/test_json.aug b/lenses/tests/test_json.aug index aec7d4c..d18b03b 100644 --- a/lenses/tests/test_json.aug +++ b/lenses/tests/test_json.aug @@ -2,6 +2,15 @@ module Test_json = let lns = Json.lns +(* Non recursive checks *) + +(* Typecheck finitely deep nesting *) +let value0 = Json.str | Json.number | Json.const /true|false|null/ +let value1 = Json.fix_value value0 +(* This test is usually too heavy, activate at will +let value2 = Json.fix_value value1 +*) + test lns get "\"menu\"" = { "string" = "menu" } test lns get "true" = { "const" = "true" } @@ -52,23 +61,32 @@ test lns get s = { "dict" { "entry" = "menu" { "dict" + { } { "entry" = "id" { "string" = "file" } } + { } { "entry" = "value" { "string" = "File" } } + { } { "entry" = "popup" { "dict" + { } { "entry" = "menuitem" { "array" + { } { "dict" { "entry" = "value" { "string" = "New" } } { "entry" = "onclick" { "string" = "CreateNewDoc()" } } } + { } { "dict" { "entry" = "value" { "string" = "Open" } } { "entry" = "onclick" { "string" = "OpenDoc()" } } } + { } { "dict" { "entry" = "value" { "string" = "Close" } } { "entry" = "onclick" { "string" = "CloseDoc()" } } - } } } } } } } } + { } + } + { } } } { } } } } } } let t = " {\"web-app\": { @@ -161,170 +179,318 @@ let t = " \"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}" test lns get t = + { } { "dict" { "entry" = "web-app" { "dict" + { } { "entry" = "servlet" { "array" + { } { "dict" + { } { "entry" = "servlet-name" { "string" = "cofaxCDS" } } + { } { "entry" = "servlet-class" { "string" = "org.cofax.cds.CDSServlet" } } + { } { "entry" = "init-param" { "dict" + { } { "entry" = "configGlossary:installationAt" { "string" = "Philadelphia, PA" } } + { } { "entry" = "configGlossary:adminEmail" { "string" = "ksm@pobox.com" } } + { } { "entry" = "configGlossary:poweredBy" { "string" = "Cofax" } } + { } { "entry" = "configGlossary:poweredByIcon" { "string" = "/images/cofax.gif" } } + { } { "entry" = "configGlossary:staticPath" { "string" = "/content/static" } } + { } { "entry" = "templateProcessorClass" { "string" = "org.cofax.WysiwygTemplate" } } + { } { "entry" = "templateLoaderClass" { "string" = "org.cofax.FilesTemplateLoader" } } + { } { "entry" = "templatePath" { "string" = "templates" } } + { } { "entry" = "templateOverridePath" { "string" = "" } } + { } { "entry" = "defaultListTemplate" { "string" = "listTemplate.htm" } } + { } { "entry" = "defaultFileTemplate" { "string" = "articleTemplate.htm" } } + { } { "entry" = "useJSP" { "const" = "false" } } + { } { "entry" = "jspListTemplate" { "string" = "listTemplate.jsp" } } + { } { "entry" = "jspFileTemplate" { "string" = "articleTemplate.jsp" } } + { } { "entry" = "cachePackageTagsTrack" { "number" = "200" } } + { } { "entry" = "cachePackageTagsStore" { "number" = "200" } } + { } { "entry" = "cachePackageTagsRefresh" { "number" = "60" } } + { } { "entry" = "cacheTemplatesTrack" { "number" = "100" } } + { } { "entry" = "cacheTemplatesStore" { "number" = "50" } } + { } { "entry" = "cacheTemplatesRefresh" { "number" = "15" } } + { } { "entry" = "cachePagesTrack" { "number" = "200" } } + { } { "entry" = "cachePagesStore" { "number" = "100" } } + { } { "entry" = "cachePagesRefresh" { "number" = "10" } } + { } { "entry" = "cachePagesDirtyRead" { "number" = "10" } } + { } { "entry" = "searchEngineListTemplate" { "string" = "forSearchEnginesList.htm" } } + { } { "entry" = "searchEngineFileTemplate" { "string" = "forSearchEngines.htm" } } + { } { "entry" = "searchEngineRobotsDb" { "string" = "WEB-INF/robots.db" } } + { } { "entry" = "useDataStore" { "const" = "true" } } + { } { "entry" = "dataStoreClass" { "string" = "org.cofax.SqlDataStore" } } + { } { "entry" = "redirectionClass" { "string" = "org.cofax.SqlRedirection" } } + { } { "entry" = "dataStoreName" { "string" = "cofax" } } + { } { "entry" = "dataStoreDriver" { "string" = "com.microsoft.jdbc.sqlserver.SQLServerDriver" } } + { } { "entry" = "dataStoreUrl" { "string" = "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon" } } + { } { "entry" = "dataStoreUser" { "string" = "sa" } } + { } { "entry" = "dataStorePassword" { "string" = "dataStoreTestQuery" } } + { } { "entry" = "dataStoreTestQuery" { "string" = "SET NOCOUNT ON;select test='test';" } } + { } { "entry" = "dataStoreLogFile" { "string" = "/usr/local/tomcat/logs/datastore.log" } } + { } { "entry" = "dataStoreInitConns" { "number" = "10" } } + { } { "entry" = "dataStoreMaxConns" { "number" = "100" } } + { } { "entry" = "dataStoreConnUsageLimit" { "number" = "100" } } + { } { "entry" = "dataStoreLogLevel" { "string" = "debug" } } + { } { "entry" = "maxUrlLength" { "number" = "500" } } } } } + { } { "dict" + { } { "entry" = "servlet-name" { "string" = "cofaxEmail" } } + { } { "entry" = "servlet-class" { "string" = "org.cofax.cds.EmailServlet" } } + { } { "entry" = "init-param" { "dict" + { } { "entry" = "mailHost" { "string" = "mail1" } } + { } { "entry" = "mailHostOverride" { "string" = "mail2" } } } } } + { } { "dict" + { } { "entry" = "servlet-name" { "string" = "cofaxAdmin" } } + { } { "entry" = "servlet-class" { "string" = "org.cofax.cds.AdminServlet" } } } + { } + { } { "dict" + { } { "entry" = "servlet-name" { "string" = "fileServlet" } } + { } { "entry" = "servlet-class" { "string" = "org.cofax.cds.FileServlet" } } } + { } { "dict" + { } { "entry" = "servlet-name" { "string" = "cofaxTools" } } + { } { "entry" = "servlet-class" { "string" = "org.cofax.cms.CofaxToolsServlet" } } + { } { "entry" = "init-param" { "dict" + { } { "entry" = "templatePath" { "string" = "toolstemplates/" } } + { } { "entry" = "log" { "number" = "1" } } + { } { "entry" = "logLocation" { "string" = "/usr/local/tomcat/logs/CofaxTools.log" } } + { } { "entry" = "logMaxSize" { "string" = "" } } + { } { "entry" = "dataLog" { "number" = "1" } } + { } { "entry" = "dataLogLocation" { "string" = "/usr/local/tomcat/logs/dataLog.log" } } + { } { "entry" = "dataLogMaxSize" { "string" = "" } } + { } { "entry" = "removePageCache" { "string" = "/content/admin/remove?cache=pages&id=" } } + { } { "entry" = "removeTemplateCache" { "string" = "/content/admin/remove?cache=templates&id=" } } + { } { "entry" = "fileTransferFolder" { "string" = "/usr/local/tomcat/webapps/content/fileTransferFolder" } } + { } { "entry" = "lookInContext" { "number" = "1" } } + { } { "entry" = "adminGroupID" { "number" = "4" } } + { } { "entry" = "betaServer" { "const" = "true" } } } } } } } + { } { "entry" = "servlet-mapping" { "dict" + { } { "entry" = "cofaxCDS" { "string" = "/" } } + { } { "entry" = "cofaxEmail" { "string" = "/cofaxutil/aemail/*" } } + { } { "entry" = "cofaxAdmin" { "string" = "/admin/*" } } + { } { "entry" = "fileServlet" { "string" = "/static/*" } } + { } { "entry" = "cofaxTools" { "string" = "/tools/*" } } } } + { } + { } { "entry" = "taglib" { "dict" + { } { "entry" = "taglib-uri" { "string" = "cofax.tld" } } + { } { "entry" = "taglib-location" { "string" = "/WEB-INF/tlds/cofax.tld" } } } } } } } + +(* Comments *) +test lns get "// A comment +// +{\"menu\": 1 } +// +/* +This is a multiline comment +*/\n" = + { "#comment" = "A comment" } + { } + { "dict" + { "entry" = "menu" + { "number" = "1" } } + { } + { } + { "#mcomment" + { "1" = "This is a multiline comment" } } } + + +let s_commented = "/* before */ +{ // before all values +\"key\": // my key + \"value\" // my value + , // after value +\"key2\": [ // before array values + \"val21\", + \"val22\" + // after value 22 +] +// after all values +} +" +test lns get s_commented = + { "#mcomment" { "1" = "before" } } + { "dict" + { "#comment" = "before all values" } + { "entry" = "key" + { "#comment" = "my key" } + { "string" = "value" { "#comment" = "my value" } } } + { "#comment" = "after value" } + { "entry" = "key2" + { "array" + { "#comment" = "before array values" } + { "string" = "val21" } + { } + { "string" = "val22" + { } + { "#comment" = "after value 22" } } + { } + { "#comment" = "after all values" } } } + { } } + +(* Test lns + Allow escaped quotes, backslashes and tabs/newlines *) +test lns get "{ \"filesystem\": \"ext3\\" \\\\ \t \r\n SEC_TYPE=\\"ext2\" }\n" = + { "dict" + { "entry" = "filesystem" + { "string" = "ext3\\" \\\\ \t \r\n SEC_TYPE=\\"ext2" } } + { } } diff --git a/lenses/tests/test_keepalived.aug b/lenses/tests/test_keepalived.aug index 371df25..6886357 100644 --- a/lenses/tests/test_keepalived.aug +++ b/lenses/tests/test_keepalived.aug @@ -7,151 +7,177 @@ module Test_Keepalived = (* Variable: conf A full configuration file *) - let conf = "! This is a comment -! Configuration File for keepalived - -global_defs { - ! this is who emails will go to on alerts - notification_email { - admins@example.com - fakepager@example.com - ! add a few more email addresses here if you would like - } - notification_email_from admins@example.com - - smtp_server 127.0.0.1 ! I use the local machine to relay mail - smtp_connect_timeout 30 - - ! each load balancer should have a different ID - ! this will be used in SMTP alerts, so you should make - ! each router easily identifiable - lvs_id LVS_EXAMPLE_01 -} - -vrrp_sync_group VG1 { - group { - inside_network # name of vrrp_instance (below) - outside_network # One for each moveable IP. - } -} - -vrrp_instance VI_1 { - state MASTER - interface eth0 - - track_interface { - eth0 # Back - eth1 # DMZ - } - track_script { - check_apache2 # weight = +2 si ok, 0 si nok - } - garp_master_delay 5 - priority 50 - advert_int 2 - authentication { - auth_type PASS - auth_pass mypass - } - virtual_ipaddress { - 10.234.66.146/32 dev eth0 - } - - lvs_sync_daemon_interface eth0 - ha_suspend - - notify_master \"/svr/scripts/notify_master.sh\" - notify_backup \"/svr/scripts/notify_backup.sh\" - notify_fault \"/svr/scripts/notify_fault.sh\" - - ! each virtual router id must be unique per instance name! - virtual_router_id 51 - - ! MASTER and BACKUP state are determined by the priority - ! even if you specify MASTER as the state, the state will - ! be voted on by priority (so if your state is MASTER but your - ! priority is lower than the router with BACKUP, you will lose - ! the MASTER state) - ! I make it a habit to set priorities at least 50 points apart - ! note that a lower number is lesser priority - lower gets less vote - priority 150 - - ! how often should we vote, in seconds? - advert_int 1 - - ! send an alert when this instance changes state from MASTER to BACKUP - smtp_alert - - ! this authentication is for syncing between failover servers - ! keepalived supports PASS, which is simple password - ! authentication - ! or AH, which is the IPSec authentication header. - ! I don't use AH - ! yet as many people have reported problems with it - authentication { - auth_type PASS - auth_pass example - } - - ! these are the IP addresses that keepalived will setup on this - ! machine. Later in the config we will specify which real - ! servers are behind these IPs - ! without this block, keepalived will not setup and takedown the - ! any IP addresses - - virtual_ipaddress { - 192.168.1.11 - 10.234.66.146/32 dev vlan933 # parse it well - ! and more if you want them - } -} - -virtual_server 192.168.1.11 22 { - delay_loop 6 - - ! use round-robin as a load balancing algorithm - lb_algo rr - - ! we are doing NAT - lb_kind NAT - nat_mask 255.255.255.0 - - protocol TCP - - sorry_server 10.20.40.30 22 - - ! there can be as many real_server blocks as you need - - real_server 10.20.40.10 22 { - - ! if we used weighted round-robin or a similar lb algo, - ! we include the weight of this server - - weight 1 - - ! here is a health checker for this server. - ! we could use a custom script here (see the keepalived docs) - ! but we will just make sure we can do a vanilla tcp connect() - ! on port 22 - ! if it fails, we will pull this realserver out of the pool - ! and send email about the removal - TCP_CHECK { - connect_timeout 3 - connect_port 22 - } - } -} + let conf = "! This is a comment +! Configuration File for keepalived + +global_defs { + ! this is who emails will go to on alerts + notification_email { + admins@example.com + fakepager@example.com + ! add a few more email addresses here if you would like + } + notification_email_from admins@example.com + + smtp_server 127.0.0.1 ! I use the local machine to relay mail + smtp_connect_timeout 30 + + ! each load balancer should have a different ID + ! this will be used in SMTP alerts, so you should make + ! each router easily identifiable + lvs_id LVS_EXAMPLE_01 + + vrrp_mcast_group4 224.0.0.18 + vrrp_mcast_group6 ff02::12 +} + +vrrp_sync_group VG1 { + group { + inside_network # name of vrrp_instance (below) + outside_network # One for each moveable IP. + } + notify /usr/bin/foo + notify_master /usr/bin/foo + smtp_alert +} + +vrrp_instance VI_1 { + state MASTER + interface eth0 + + track_interface { + eth0 # Back + eth1 # DMZ + } + track_script { + check_apache2 # weight = +2 si ok, 0 si nok + } + garp_master_delay 5 + garp_master_repeat 5 + garp_master_refresh 5 + garp_master_refresh_repeat 5 + priority 50 + advert_int 2 + authentication { + auth_type PASS + auth_pass mypass + } + virtual_ipaddress { + 10.234.66.146/32 dev eth0 + } + + lvs_sync_daemon_interface eth0 + ha_suspend + + notify_master \"/svr/scripts/notify_master.sh\" + notify_backup \"/svr/scripts/notify_backup.sh\" + notify_fault \"/svr/scripts/notify_fault.sh\" + notify \"/svr/scripts/notify.sh\" + + ! each virtual router id must be unique per instance name! + virtual_router_id 51 + + ! MASTER and BACKUP state are determined by the priority + ! even if you specify MASTER as the state, the state will + ! be voted on by priority (so if your state is MASTER but your + ! priority is lower than the router with BACKUP, you will lose + ! the MASTER state) + ! I make it a habit to set priorities at least 50 points apart + ! note that a lower number is lesser priority - lower gets less vote + priority 150 + + ! how often should we vote, in seconds? + advert_int 1 + + ! send an alert when this instance changes state from MASTER to BACKUP + smtp_alert + + ! this authentication is for syncing between failover servers + ! keepalived supports PASS, which is simple password + ! authentication + ! or AH, which is the IPSec authentication header. + ! I don't use AH + ! yet as many people have reported problems with it + authentication { + auth_type PASS + auth_pass example + } + + ! these are the IP addresses that keepalived will setup on this + ! machine. Later in the config we will specify which real + ! servers are behind these IPs + ! without this block, keepalived will not setup and takedown the + ! any IP addresses + + virtual_ipaddress { + 192.168.1.11 + 10.234.66.146/32 dev vlan933 # parse it well + ! and more if you want them + } + + use_vmac + vmac_xmit_base + native_ipv6 + dont_track_primary + preempt_delay + + mcast_src_ip 192.168.1.1 + unicast_src_ip 192.168.1.1 + + unicast_peer { + 192.168.1.2 + 192.168.1.3 + } +} + +virtual_server 192.168.1.11 22 { + delay_loop 6 + + ! use round-robin as a load balancing algorithm + lb_algo rr + + ! we are doing NAT + lb_kind NAT + nat_mask 255.255.255.0 + + protocol TCP + + sorry_server 10.20.40.30 22 + + ! there can be as many real_server blocks as you need + + real_server 10.20.40.10 22 { + + ! if we used weighted round-robin or a similar lb algo, + ! we include the weight of this server + + weight 1 + + ! here is a health checker for this server. + ! we could use a custom script here (see the keepalived docs) + ! but we will just make sure we can do a vanilla tcp connect() + ! on port 22 + ! if it fails, we will pull this realserver out of the pool + ! and send email about the removal + TCP_CHECK { + connect_timeout 3 + connect_port 22 + } + } +} virtual_server_group DNS_1 { - 192.168.0.1 22 - 10.234.55.22-25 36 - 10.45.58.59/32 27 + 192.168.0.1 22 + 10.234.55.22-25 36 + 10.45.58.59/32 27 } vrrp_script chk_apache2 { # Requires keepalived-1.1.13 -script \"killall -0 apache2\" # faster -interval 2 # check every 2 seconds -weight 2 # add 2 points of prio if OK + script \"killall -0 apache2\" # faster + interval 2 # check every 2 seconds + weight 2 # add 2 points of prio if OK + fall 5 + raise 5 } ! that's all @@ -167,9 +193,9 @@ weight 2 # add 2 points of prio if OK { "global_defs" { "#comment" = "this is who emails will go to on alerts" } { "notification_email" - { "email" = "admins@example.com" } - { "email" = "fakepager@example.com" } - { "#comment" = "add a few more email addresses here if you would like" } } + { "email" = "admins@example.com" } + { "email" = "fakepager@example.com" } + { "#comment" = "add a few more email addresses here if you would like" } } { "notification_email_from" = "admins@example.com" } { } { "smtp_server" = "127.0.0.1" @@ -179,14 +205,20 @@ weight 2 # add 2 points of prio if OK { "#comment" = "each load balancer should have a different ID" } { "#comment" = "this will be used in SMTP alerts, so you should make" } { "#comment" = "each router easily identifiable" } - { "lvs_id" = "LVS_EXAMPLE_01" } } + { "lvs_id" = "LVS_EXAMPLE_01" } + {} + { "vrrp_mcast_group4" = "224.0.0.18" } + { "vrrp_mcast_group6" = "ff02::12" } } {} { "vrrp_sync_group" = "VG1" { "group" { "inside_network" { "#comment" = "name of vrrp_instance (below)" } } { "outside_network" - { "#comment" = "One for each moveable IP." } } } } + { "#comment" = "One for each moveable IP." } } } + { "notify" = "/usr/bin/foo" } + { "notify_master" = "/usr/bin/foo" } + { "smtp_alert" } } {} { "vrrp_instance" = "VI_1" { "state" = "MASTER" } @@ -198,6 +230,9 @@ weight 2 # add 2 points of prio if OK { "track_script" { "check_apache2" { "#comment" = "weight = +2 si ok, 0 si nok" } } } { "garp_master_delay" = "5" } + { "garp_master_repeat" = "5" } + { "garp_master_refresh" = "5" } + { "garp_master_refresh_repeat" = "5" } { "priority" = "50" } { "advert_int" = "2" } { "authentication" @@ -214,6 +249,7 @@ weight 2 # add 2 points of prio if OK { "notify_master" = "\"/svr/scripts/notify_master.sh\"" } { "notify_backup" = "\"/svr/scripts/notify_backup.sh\"" } { "notify_fault" = "\"/svr/scripts/notify_fault.sh\"" } + { "notify" = "\"/svr/scripts/notify.sh\"" } { } { "#comment" = "each virtual router id must be unique per instance name!" } { "virtual_router_id" = "51" } @@ -255,57 +291,70 @@ weight 2 # add 2 points of prio if OK { "prefixlen" = "32" } { "dev" = "vlan933" } { "#comment" = "parse it well" } } - { "#comment" = "and more if you want them" } } } + { "#comment" = "and more if you want them" } } { } - { "virtual_server" - { "ip" = "192.168.1.11" } + { "use_vmac" } + { "vmac_xmit_base" } + { "native_ipv6" } + { "dont_track_primary" } + { "preempt_delay" } + { } + { "mcast_src_ip" = "192.168.1.1" } + { "unicast_src_ip" = "192.168.1.1" } + { } + { "unicast_peer" + { "ipaddr" = "192.168.1.2" } + { "ipaddr" = "192.168.1.3" } } } + { } + { "virtual_server" + { "ip" = "192.168.1.11" } + { "port" = "22" } + { "delay_loop" = "6" } + { } + { "#comment" = "use round-robin as a load balancing algorithm" } + { "lb_algo" = "rr" } + { } + { "#comment" = "we are doing NAT" } + { "lb_kind" = "NAT" } + { "nat_mask" = "255.255.255.0" } + { } + { "protocol" = "TCP" } + { } + { "sorry_server" + { "ip" = "10.20.40.30" } + { "port" = "22" } } + { } + { "#comment" = "there can be as many real_server blocks as you need" } + { } + { "real_server" + { "ip" = "10.20.40.10" } { "port" = "22" } - { "delay_loop" = "6" } - { } - { "#comment" = "use round-robin as a load balancing algorithm" } - { "lb_algo" = "rr" } + { "#comment" = "if we used weighted round-robin or a similar lb algo," } + { "#comment" = "we include the weight of this server" } { } - { "#comment" = "we are doing NAT" } - { "lb_kind" = "NAT" } - { "nat_mask" = "255.255.255.0" } + { "weight" = "1" } { } - { "protocol" = "TCP" } - { } - { "sorry_server" - { "ip" = "10.20.40.30" } - { "port" = "22" } } - { } - { "#comment" = "there can be as many real_server blocks as you need" } - { } - { "real_server" - { "ip" = "10.20.40.10" } - { "port" = "22" } - { "#comment" = "if we used weighted round-robin or a similar lb algo," } - { "#comment" = "we include the weight of this server" } - { } - { "weight" = "1" } - { } - { "#comment" = "here is a health checker for this server." } - { "#comment" = "we could use a custom script here (see the keepalived docs)" } - { "#comment" = "but we will just make sure we can do a vanilla tcp connect()" } - { "#comment" = "on port 22" } - { "#comment" = "if it fails, we will pull this realserver out of the pool" } - { "#comment" = "and send email about the removal" } - { "TCP_CHECK" - { "connect_timeout" = "3" } - { "connect_port" = "22" } } } } + { "#comment" = "here is a health checker for this server." } + { "#comment" = "we could use a custom script here (see the keepalived docs)" } + { "#comment" = "but we will just make sure we can do a vanilla tcp connect()" } + { "#comment" = "on port 22" } + { "#comment" = "if it fails, we will pull this realserver out of the pool" } + { "#comment" = "and send email about the removal" } + { "TCP_CHECK" + { "connect_timeout" = "3" } + { "connect_port" = "22" } } } } { } { "virtual_server_group" = "DNS_1" { "vip" - { "ipaddr" = "192.168.0.1" } - { "port" = "22" } } - { "vip" - { "ipaddr" = "10.234.55.22-25" } - { "port" = "36" } } - { "vip" - { "ipaddr" = "10.45.58.59" - { "prefixlen" = "32" } } - { "port" = "27" } } } + { "ipaddr" = "192.168.0.1" } + { "port" = "22" } } + { "vip" + { "ipaddr" = "10.234.55.22-25" } + { "port" = "36" } } + { "vip" + { "ipaddr" = "10.45.58.59" + { "prefixlen" = "32" } } + { "port" = "27" } } } { } { "vrrp_script" = "chk_apache2" { "#comment" = "Requires keepalived-1.1.13" } @@ -314,20 +363,22 @@ weight 2 # add 2 points of prio if OK { "interval" = "2" { "#comment" = "check every 2 seconds" } } { "weight" = "2" - { "#comment" = "add 2 points of prio if OK" } } } + { "#comment" = "add 2 points of prio if OK" } } + { "fall" = "5" } + { "raise" = "5" } } { } { "#comment" = "that's all" } (* Variable: tcp_check An example of a TCP health checker *) let tcp_check = "virtual_server 192.168.1.11 22 { - real_server 10.20.40.10 22 { - TCP_CHECK { - connect_timeout 3 - connect_port 22 - bindto 192.168.1.1 - } + real_server 10.20.40.10 22 { + TCP_CHECK { + connect_timeout 3 + connect_port 22 + bindto 192.168.1.1 } + } } " test Keepalived.lns get tcp_check = @@ -345,13 +396,13 @@ test Keepalived.lns get tcp_check = (* Variable: misc_check An example of a MISC health checker *) let misc_check = "virtual_server 192.168.1.11 22 { - real_server 10.20.40.10 22 { - MISC_CHECK { - misc_path /usr/local/bin/server_test - misc_timeout 3 - misc_dynamic - } + real_server 10.20.40.10 22 { + MISC_CHECK { + misc_path /usr/local/bin/server_test + misc_timeout 3 + misc_dynamic } + } } " test Keepalived.lns get misc_check = @@ -369,19 +420,19 @@ test Keepalived.lns get misc_check = (* Variable: smtp_check An example of an SMTP health checker *) let smtp_check = "virtual_server 192.168.1.11 22 { - real_server 10.20.40.10 22 { - SMTP_CHECK { - host { - connect_ip 10.20.40.11 - connect_port 587 - bindto 192.168.1.1 - } - connect_timeout 3 - retry 5 - delay_before_retry 10 - helo_name \"Testing Augeas\" - } + real_server 10.20.40.10 22 { + SMTP_CHECK { + host { + connect_ip 10.20.40.11 + connect_port 587 + bindto 192.168.1.1 + } + connect_timeout 3 + retry 5 + delay_before_retry 10 + helo_name \"Testing Augeas\" } + } } " test Keepalived.lns get smtp_check = @@ -404,22 +455,22 @@ test Keepalived.lns get smtp_check = (* Variable: http_check An example of an HTTP health checker *) let http_check = "virtual_server 192.168.1.11 22 { - real_server 10.20.40.10 22 { - HTTP_GET { - url { - path /mrtg2/ - digest 9b3a0c85a887a256d6939da88aabd8cd - status_code 200 - } - connect_timeout 3 - connect_port 8080 - nb_get_retry 5 - delay_before_retry 10 - } - SSL_GET { - connect_port 8443 - } + real_server 10.20.40.10 22 { + HTTP_GET { + url { + path /mrtg2/ + digest 9b3a0c85a887a256d6939da88aabd8cd + status_code 200 + } + connect_timeout 3 + connect_port 8080 + nb_get_retry 5 + delay_before_retry 10 + } + SSL_GET { + connect_port 8443 } + } } " test Keepalived.lns get http_check = diff --git a/lenses/tests/test_known_hosts.aug b/lenses/tests/test_known_hosts.aug index 6f46a57..1b987e3 100644 --- a/lenses/tests/test_known_hosts.aug +++ b/lenses/tests/test_known_hosts.aug @@ -8,15 +8,41 @@ module Test_Known_Hosts = (* Test: Known_Hosts.lns Simple get test *) test Known_Hosts.lns get "# A comment -foo.example.com,foo,bar ecdsa-sha2-nistp256 AAABBBDKDFX= +foo.example.com,foo ecdsa-sha2-nistp256 AAABBBDKDFX= +bar.example.com,bar ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN9NJSjDZh4+K6WBS16iX7ZndnwbGsaEbLwHlCEhZmef |1|FhUqf1kMlRWNfK6InQSAmXiNiSY=|jwbKFwD4ipl6D0k6OoshmW7xOao= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIvNOU8OedkWalFmoFcJWP3nasnCLx6M78F9y0rzTQtplggNd0dvR0A4SQOBfHInmk5dH6YGGcpT3PM3cJBR7rI=\n" = { "#comment" = "A comment" } { "1" = "foo.example.com" { "alias" = "foo" } - { "alias" = "bar" } { "type" = "ecdsa-sha2-nistp256" } { "key" = "AAABBBDKDFX=" } } - { "2" = "|1|FhUqf1kMlRWNfK6InQSAmXiNiSY=|jwbKFwD4ipl6D0k6OoshmW7xOao=" + { "2" = "bar.example.com" + { "alias" = "bar" } + { "type" = "ssh-ed25519" } + { "key" = "AAAAC3NzaC1lZDI1NTE5AAAAIN9NJSjDZh4+K6WBS16iX7ZndnwbGsaEbLwHlCEhZmef" } } + { "3" = "|1|FhUqf1kMlRWNfK6InQSAmXiNiSY=|jwbKFwD4ipl6D0k6OoshmW7xOao=" { "type" = "ecdsa-sha2-nistp256" } { "key" = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIvNOU8OedkWalFmoFcJWP3nasnCLx6M78F9y0rzTQtplggNd0dvR0A4SQOBfHInmk5dH6YGGcpT3PM3cJBR7rI=" } } +(* Test: Known_Hosts.lns + Markers *) +test Known_Hosts.lns get "@revoked * ssh-rsa AAAAB5W +@cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W\n" = + { "1" = "*" + { "@revoked" } + { "type" = "ssh-rsa" } + { "key" = "AAAAB5W" } } + { "2" = "*.mydomain.org" + { "@cert-authority" } + { "alias" = "*.mydomain.com" } + { "type" = "ssh-rsa" } + { "key" = "AAAAB5W" } } + +(* Test: Known_Hosts.lns + Eol comment *) +test Known_Hosts.lns get "@revoked * ssh-rsa AAAAB5W # this is revoked\n" = + { "1" = "*" + { "@revoked" } + { "type" = "ssh-rsa" } + { "key" = "AAAAB5W" } + { "#comment" = "this is revoked" } } diff --git a/lenses/tests/test_masterpasswd.aug b/lenses/tests/test_masterpasswd.aug new file mode 100644 index 0000000..838b7bd --- /dev/null +++ b/lenses/tests/test_masterpasswd.aug @@ -0,0 +1,125 @@ +module Test_MasterPasswd = + +let conf = "root:*:0:0:daemon:0:0:Charlie &:/root:/bin/ksh +sshd:*:27:27::0:0:sshd privsep:/var/empty:/sbin/nologin +_portmap:*:28:28::0:0:portmap:/var/empty:/sbin/nologin +test:x:1000:1000:ldap:1434329080:1434933880:Test User,,,:/home/test:/bin/bash +" + +test MasterPasswd.lns get conf = + { "root" + { "password" = "*" } + { "uid" = "0" } + { "gid" = "0" } + { "class" = "daemon" } + { "change_date" = "0" } + { "expire_date" = "0" } + { "name" = "Charlie &" } + { "home" = "/root" } + { "shell" = "/bin/ksh" } } + { "sshd" + { "password" = "*" } + { "uid" = "27" } + { "gid" = "27" } + { "class" } + { "change_date" = "0" } + { "expire_date" = "0" } + { "name" = "sshd privsep" } + { "home" = "/var/empty" } + { "shell" = "/sbin/nologin" } } + { "_portmap" + { "password" = "*" } + { "uid" = "28" } + { "gid" = "28" } + { "class" } + { "change_date" = "0" } + { "expire_date" = "0" } + { "name" = "portmap" } + { "home" = "/var/empty" } + { "shell" = "/sbin/nologin" } } + { "test" + { "password" = "x" } + { "uid" = "1000" } + { "gid" = "1000" } + { "class" = "ldap" } + { "change_date" = "1434329080" } + { "expire_date" = "1434933880" } + { "name" = "Test User,,," } + { "home" = "/home/test" } + { "shell" = "/bin/bash" } } + +(* Popular on Solaris *) +test MasterPasswd.lns get "+@some-nis-group:::::::::\n" = + { "@nis" = "some-nis-group" } + +test MasterPasswd.lns get "+\n" = + { "@nisdefault" } + +test MasterPasswd.lns get "+:::::::::\n" = + { "@nisdefault" + { "password" = "" } + { "uid" = "" } + { "gid" = "" } + { "class" } + { "change_date" = "" } + { "expire_date" = "" } + { "name" } + { "home" } + { "shell" } } + +test MasterPasswd.lns get "+:::::::::/sbin/nologin\n" = + { "@nisdefault" + { "password" = "" } + { "uid" = "" } + { "gid" = "" } + { "class" } + { "change_date" = "" } + { "expire_date" = "" } + { "name" } + { "home" } + { "shell" = "/sbin/nologin" } } + +test MasterPasswd.lns get "+:*:::ldap:::::\n" = + { "@nisdefault" + { "password" = "*" } + { "uid" = "" } + { "gid" = "" } + { "class" = "ldap" } + { "change_date" = "" } + { "expire_date" = "" } + { "name" } + { "home" } + { "shell" } } + +(* NIS entries with overrides, ticket #339 *) +test MasterPasswd.lns get "+@bob::::::::/home/bob:/bin/bash\n" = + { "@nis" = "bob" + { "home" = "/home/bob" } + { "shell" = "/bin/bash" } } + +(* NIS user entries *) +test MasterPasswd.lns get "+bob:::::::::\n" = + { "@+nisuser" = "bob" } + +test MasterPasswd.lns get "+bob:::::::User Comment:/home/bob:/bin/bash\n" = + { "@+nisuser" = "bob" + { "name" = "User Comment" } + { "home" = "/home/bob" } + { "shell" = "/bin/bash" } } + +test MasterPasswd.lns put "+bob:::::::::\n" after + set "@+nisuser" "alice" += "+alice:::::::::\n" + +test MasterPasswd.lns put "+bob:::::::::\n" after + set "@+nisuser/name" "User Comment"; + set "@+nisuser/home" "/home/bob"; + set "@+nisuser/shell" "/bin/bash" += "+bob:::::::User Comment:/home/bob:/bin/bash\n" + +test MasterPasswd.lns get "-bob:::::::::\n" = + { "@-nisuser" = "bob" } + +test MasterPasswd.lns put "-bob:::::::::\n" after + set "@-nisuser" "alice" += "-alice:::::::::\n" diff --git a/lenses/tests/test_multipath.aug b/lenses/tests/test_multipath.aug index 6dcfdce..02c95eb 100644 --- a/lenses/tests/test_multipath.aug +++ b/lenses/tests/test_multipath.aug @@ -40,6 +40,15 @@ defaults { reassign_maps yes fast_io_fail_tmo 5 async_timeout 5 + flush_on_last_del no + delay_watch_checks no + delay_wait_checks no + find_multipaths yes + checker_timeout 10 + hwtable_regex_match yes + reload_readwrite no + force_sync yes + config_dir /etc/multipath/conf.d } # Sections without empty lines in between @@ -61,6 +70,7 @@ multipaths { failback manual rr_weight priorities no_path_retry 5 + flush_on_last_del yes } multipath { wwid 1DEC_____321816758474 @@ -80,12 +90,15 @@ devices { rr_weight priorities rr_min_io_rq 75 no_path_retry queue + reservation_key a12345 } device { vendor \"COMPAQ \" product \"MSA1000 \" path_grouping_policy multibus polling_interval 9 + delay_watch_checks 10 + delay_wait_checks 10 } }\n" @@ -126,7 +139,16 @@ test Multipath.lns get conf = { "verbosity" = "2" } { "reassign_maps" = "yes" } { "fast_io_fail_tmo" = "5" } - { "async_timeout" = "5" } } + { "async_timeout" = "5" } + { "flush_on_last_del" = "no" } + { "delay_watch_checks" = "no" } + { "delay_wait_checks" = "no" } + { "find_multipaths" = "yes" } + { "checker_timeout" = "10" } + { "hwtable_regex_match" = "yes" } + { "reload_readwrite" = "no" } + { "force_sync" = "yes" } + { "config_dir" = "/etc/multipath/conf.d" } } { } { "#comment" = "Sections without empty lines in between" } { "blacklist" @@ -145,7 +167,8 @@ test Multipath.lns get conf = { "path_selector" = "round-robin 0" } { "failback" = "manual" } { "rr_weight" = "priorities" } - { "no_path_retry" = "5" } } + { "no_path_retry" = "5" } + { "flush_on_last_del" = "yes" } } { "multipath" { "wwid" = "1DEC_____321816758474" } { "alias" = "red" } } } @@ -161,9 +184,12 @@ test Multipath.lns get conf = { "failback" = "15" } { "rr_weight" = "priorities" } { "rr_min_io_rq" = "75" } - { "no_path_retry" = "queue" } } + { "no_path_retry" = "queue" } + { "reservation_key" = "a12345" } } { "device" { "vendor" = "COMPAQ " } { "product" = "MSA1000 " } { "path_grouping_policy" = "multibus" } - { "polling_interval" = "9" } } } + { "polling_interval" = "9" } + { "delay_watch_checks" = "10" } + { "delay_wait_checks" = "10" } } } \ No newline at end of file diff --git a/lenses/tests/test_nginx.aug b/lenses/tests/test_nginx.aug index 7cdf985..f70ff52 100644 --- a/lenses/tests/test_nginx.aug +++ b/lenses/tests/test_nginx.aug @@ -4,6 +4,13 @@ Module: Test_Nginx *) module Test_nginx = +(* Check for non-recursive ambiguities *) +let directive = Nginx.simple + | Nginx.block ( + Nginx.simple + | Nginx.block Nginx.simple + ) + (* Do some limited typechecking on the recursive lens; note that unrolling once more leads to a typecheck error that seems to be spurious, though it's not clear why @@ -199,6 +206,35 @@ test lns get http = { "root" = "html" } } } { "gzip" = "on" } } + +(* GH #335 - server single line entries *) +let http_server_single_line_entries = "http { + upstream big_server_com { + server 127.0.0.3:8000 weight=5; + server 127.0.0.3:8001 weight=5; + server 192.168.0.1:8000; + server 192.168.0.1:8001; + server backend2.example.com:8080 fail_timeout=5s slow_start=30s; + server backend3.example.com resolve; + } +}\n" + +test lns get http_server_single_line_entries = + { "http" + { "upstream" + { "#name" = "big_server_com" } + { "@server" { "@address" = "127.0.0.3:8000" } { "weight" = "5" } } + { "@server" { "@address" = "127.0.0.3:8001" } { "weight" = "5" } } + { "@server" { "@address" = "192.168.0.1:8000" } } + { "@server" { "@address" = "192.168.0.1:8001" } } + { "@server" + { "@address" = "backend2.example.com:8080" } + { "fail_timeout" = "5s" } + { "slow_start" = "30s" } } + { "@server" + { "@address" = "backend3.example.com" } + { "resolve" } } } } + (* Make sure we do not screw up the indentation of the file *) test lns put http after set "/http/gzip" "off" = "http { @@ -210,3 +246,32 @@ test lns put http after set "/http/gzip" "off" = } gzip off; }\n" + +(* Test lns + GH #260 *) +test lns get "http { + geo $geo { + default 0; + + 127.0.0.1 2; + 192.168.1.0/24 1; + 10.1.0.0/16 1; + + ::1 2; + 2001:0db8::/32 1; + } +}\n" = + { "http" + { "geo" + { "#geo" = "$geo" } + { "default" = "0" } + { } + { "127.0.0.1" = "2" } + { "192.168.1.0" = "1" + { "mask" = "24" } } + { "10.1.0.0" = "1" + { "mask" = "16" } } + { } + { "::1" = "2" } + { "2001:0db8::" = "1" + { "mask" = "32" } } } } diff --git a/lenses/tests/test_ntp.aug b/lenses/tests/test_ntp.aug index c806eba..e581f31 100644 --- a/lenses/tests/test_ntp.aug +++ b/lenses/tests/test_ntp.aug @@ -24,6 +24,9 @@ statistics loopstats peerstats clockstats filegen loopstats file loopstats type day enable link filegen peerstats file peerstats type day disable filegen clockstats file clockstats type day enable nolink + +interface ignore wildcard +interface listen 127.0.0.1 " test Ntp.lns get conf = @@ -69,6 +72,13 @@ filegen clockstats file clockstats type day enable nolink { "type" = "day" } { "enable" = "enable" } { "link" = "nolink" } } + { } + { "interface" + { "action" = "ignore" } + { "addresses" = "wildcard" } } + { "interface" + { "action" = "listen" } + { "addresses" = "127.0.0.1" } } (* Some things needed to process the default ntp.conf on Fedora *) test Ntp.lns get @@ -155,4 +165,4 @@ test Ntp.tinker get "tinker panic 0 huffpuff 3.14\n" = (* Bug #297: tos directive *) test Ntp.tos get "tos maxdist 16\n" = { "tos" - { "maxdist" = "16" } } \ No newline at end of file + { "maxdist" = "16" } } diff --git a/lenses/tests/test_openshift_quickstarts.aug b/lenses/tests/test_openshift_quickstarts.aug index 7969dfc..017a92a 100644 --- a/lenses/tests/test_openshift_quickstarts.aug +++ b/lenses/tests/test_openshift_quickstarts.aug @@ -116,30 +116,38 @@ let new_conf = "[ (* Test: OpenShift_Quickstarts.lns *) test OpenShift_Quickstarts.lns get conf = { "array" + { } { "dict" { "entry" = "quickstart" { "dict" + { } { "entry" = "id" { "string" = "1" } } + { } { "entry" = "name" { "string" = "CakePHP" } } + { } { "entry" = "website" { "string" = "http://cakephp.org/" } } + { } { "entry" = "initial_git_url" { "string" = "git://github.com/openshift/cakephp-example.git" } } + { } { "entry" = "cartridges" { "array" { "string" = "php-5.4" } { "string" = "mysql-5.1" } } } + { } { "entry" = "summary" { "string" = "CakePHP is a rapid development framework for PHP which uses commonly known design patterns like Active Record, Association Data Mapping, Front Controller and MVC." } } + { } { "entry" = "tags" { "array" { "string" = "php" } @@ -147,35 +155,44 @@ test OpenShift_Quickstarts.lns get conf = { "string" = "framework" } } } + { } { "entry" = "admin_tags" { "array" } } } } } + { } { "dict" { "entry" = "quickstart" { "dict" + { } { "entry" = "id" { "string" = "2" } } + { } { "entry" = "name" { "string" = "Django" } } + { } { "entry" = "website" { "string" = "https://www.djangoproject.com/" } } + { } { "entry" = "initial_git_url" { "string" = "git://github.com/openshift/django-example.git" } } + { } { "entry" = "cartridges" { "array" { "string" = "python-2.7" } } } + { } { "entry" = "summary" { "string" = "A high-level Python web framework that encourages rapid development and clean, pragmatic design. Administrator user name and password are written to $OPENSHIFT_DATA_DIR/CREDENTIALS." } } + { } { "entry" = "tags" { "array" { "string" = "python" } @@ -183,36 +200,45 @@ test OpenShift_Quickstarts.lns get conf = { "string" = "framework" } } } + { } { "entry" = "admin_tags" { "array" } } } } } + { } { "dict" { "entry" = "quickstart" { "dict" + { } { "entry" = "id" { "string" = "4" } } + { } { "entry" = "name" { "string" = "Drupal" } } + { } { "entry" = "website" { "string" = "http://drupal.org/" } } + { } { "entry" = "initial_git_url" { "string" = "git://github.com/openshift/drupal-example.git" } } + { } { "entry" = "cartridges" { "array" { "string" = "php-5.4" } { "string" = "mysql-5.1" } } } + { } { "entry" = "summary" { "string" = "An open source content management platform written in PHP powering millions of websites and applications. It is built, used, and supported by an active and diverse community of people around the world. Administrator user name and password are written to $OPENSHIFT_DATA_DIR/CREDENTIALS." } } + { } { "entry" = "tags" { "array" { "string" = "php" } @@ -222,36 +248,45 @@ test OpenShift_Quickstarts.lns get conf = { "string" = "instant_app" } } } + { } { "entry" = "admin_tags" { "array" } } } } } + { } { "dict" { "entry" = "quickstart" { "dict" + { } { "entry" = "id" { "string" = "6" } } + { } { "entry" = "name" { "string" = "Ruby on Rails" } } + { } { "entry" = "website" { "string" = "http://rubyonrails.org/" } } + { } { "entry" = "initial_git_url" { "string" = "git://github.com/openshift/rails-example.git" } } + { } { "entry" = "cartridges" { "array" { "string" = "ruby-1.9" } { "string" = "mysql-5.1" } } } + { } { "entry" = "summary" { "string" = "An open source web framework for Ruby that is optimized for programmer happiness and sustainable productivity. It lets you write beautiful code by favoring convention over configuration." } } + { } { "entry" = "tags" { "array" { "string" = "ruby" } @@ -259,36 +294,45 @@ test OpenShift_Quickstarts.lns get conf = { "string" = "framework" } } } + { } { "entry" = "admin_tags" { "array" } } } } } + { } { "dict" { "entry" = "quickstart" { "dict" + { } { "entry" = "id" { "string" = "8" } } + { } { "entry" = "name" { "string" = "WordPress" } } + { } { "entry" = "website" { "string" = "http://wordpress.org" } } + { } { "entry" = "initial_git_url" { "string" = "git://github.com/openshift/wordpress-example.git" } } + { } { "entry" = "cartridges" { "array" { "string" = "php-5.4" } { "string" = "mysql-5.1" } } } + { } { "entry" = "summary" { "string" = "A semantic personal publishing platform written in PHP with a MySQL back end, focusing on aesthetics, web standards, and usability. Administrator user name and password are written to $OPENSHIFT_DATA_DIR/CREDENTIALS." } } + { } { "entry" = "tags" { "array" { "string" = "php" } @@ -298,11 +342,13 @@ test OpenShift_Quickstarts.lns get conf = { "string" = "instant_app" } } } + { } { "entry" = "admin_tags" { "array" } } } } + { } } } diff --git a/lenses/tests/test_openvpn.aug b/lenses/tests/test_openvpn.aug index 71170a9..f303a30 100644 --- a/lenses/tests/test_openvpn.aug +++ b/lenses/tests/test_openvpn.aug @@ -48,8 +48,6 @@ log-append openvpn.log verb 3 mute 20 management 10.0.5.20 1193 /etc/openvpn/mpass -script-security 3 system -mssfix 1300 " test OpenVPN.lns get server_conf = @@ -74,7 +72,9 @@ test OpenVPN.lns get server_conf = { "server" { "address" = "10.8.0.0" } { "netmask" = "255.255.255.0" } } - { "ifconfig-pool-persist" = "ipp.txt" } + { "ifconfig-pool-persist" + { "file" = "ipp.txt" } + } {} { "client-config-dir" = "/etc/openvpn/ccd" } { "server-bridge" @@ -109,7 +109,9 @@ test OpenVPN.lns get server_conf = { "group" = "nobody" } { "persist-key" } { "persist-tun" } - { "status" = "openvpn-status.log" } + { "status" + { "file" = "openvpn-status.log" } + } { "log" = "openvpn.log" } { "log-append" = "openvpn.log" } { "verb" = "3" } @@ -118,8 +120,6 @@ test OpenVPN.lns get server_conf = { "server" = "10.0.5.20" } { "port" = "1193" } { "pwfile" = "/etc/openvpn/mpass" } } - { "script-security" = "3 system" } - { "mssfix" = "1300" } @@ -132,11 +132,8 @@ resolv-retry infinite nobind http-proxy-retry # retry on connection failures http-proxy mytest 1024 -http-proxy mytest2 -http-proxy mute-replay-warnings ns-cert-type server -mssfix 1350 " test OpenVPN.lns get client_conf = @@ -154,11 +151,1104 @@ test OpenVPN.lns get client_conf = { "http-proxy" { "server" = "mytest" } { "port" = "1024" } } + { "mute-replay-warnings" } + { "ns-cert-type" = "server" } + +(* Most (hopefully all) permutations for OpenVPN 2.3 + * NOTE: This completely ignores IPv6 because it's hard to tell which OpenVPN + * options actually work with IPv6. Thar be dragons. + *) +let all_permutations_conf = " +config /a/canonical/file +config relative_file +mode p2p +mode server +local 192.168.1.1 +local hostname +remote 192.168.1.1 1234 +remote hostname 1234 +remote hostname +remote 192.168.1.1 +remote hostname 1234 tcp +remote 192.168.1.1 1234 tcp +remote hostname 1234 udp +remote-random-hostname +#comment square blocks should go here +proto-force udp +proto-force tcp +remote-random +proto udp +proto tcp-client +proto tcp-server +connect-retry 5 +connect-timeout 10 +connect-retry-max 0 +show-proxy-settings +http-proxy servername 1234 +http-proxy servername 1234 auto +http-proxy servername 1234 auto-nct +http-proxy servername 1234 auto none +http-proxy servername 1234 auto basic +http-proxy servername 1234 auto ntlm +http-proxy servername 1234 relative_filename ntlm +http-proxy servername 1234 /canonical/filename basic +http-proxy-retry +http-proxy-timeout 5 +http-proxy-option VERSION 1.0 +http-proxy-option AGENT an unquoted string with spaces +http-proxy-option AGENT an_unquoted_string_without_spaces +socks-proxy servername +socks-proxy servername 1234 +socks-proxy servername 1234 /canonical/file +socks-proxy servername 1234 relative/file +socks-proxy-retry +resolv-retry 5 +float +ipchange my command goes here +port 1234 +lport 1234 +rport 1234 +bind +nobind +dev tun +dev tun0 +dev tap +dev tap0 +dev null +dev-type tun +dev-type tap +topology net30 +topology p2p +topology subnet +tun-ipv6 +dev-node /canonical/file +dev-node relative/file +lladdr 1.2.3.4 +iproute my command goes here +ifconfig 1.2.3.4 5.6.7.8 +ifconfig-noexec +ifconfig-nowarn +route 111.222.123.123 +route networkname +route vpn_gateway +route net_gateway +route remote_host +route 111.222.123.123 255.123.255.221 +route 111.222.123.123 default +route 111.222.123.123 255.123.255.231 111.222.123.1 +route 111.222.123.123 default 111.222.123.1 +route 111.222.123.123 255.123.255.231 default +route 111.222.123.123 default default +route 111.222.123.123 255.123.255.231 gatewayname +route 111.222.123.123 255.123.255.231 gatewayname 5 +route 111.222.123.123 255.123.255.231 vpn_gateway +route 111.222.123.123 255.123.255.231 net_gateway +route 111.222.123.123 255.123.255.231 remote_host +route 111.222.123.123 255.123.255.231 111.222.123.1 +route 111.222.123.123 255.123.255.231 111.222.123.1 5 +max-routes 5 +route-gateway gateway-name +route-gateway 111.222.123.1 +route-gateway dhcp +route-metric 5 +route-delay +route-delay 1 +route-delay 1 2 +route-up my command goes here +route-pre-down my command goes here +route-noexec +route-nopull +allow-pull-fqdn +client-nat snat 1.2.3.4 5.6.7.8 9.8.7.6 +client-nat dnat 1.2.3.4 5.6.7.8 9.8.7.6 +redirect-gateway local +redirect-gateway local autolocal +redirect-gateway local autolocal def1 bypass-dhcp bypass-dns block-local +link-mtu 5 +redirect-private local +redirect-private local autolocal +redirect-private local autolocal def1 bypass-dhcp bypass-dns block-local +tun-mtu 5 +tun-mtu-extra 5 +mtu-disc no +mtu-disc maybe +mtu-disc yes +mtu-test +fragment 5 +mssfix 1600 +sndbuf 65536 +rcvbuf 65535 +mark blahvalue +socket-flags TCP_NODELAY +txqueuelen 5 +shaper 50 +inactive 5 +inactive 5 1024 +ping 10 +ping-exit 10 +ping-restart 10 +keepalive 1 2 +ping-timer-rem +persist-tun +persist-key +persist-local-ip +persist-remote-ip +mlock +up my command goes here +up-delay +down my command goes here +down-pre +up-restart +setenv myname myvalue +setenv my0-_name my value with spaces +setenv-safe myname myvalue +setenv-safe my-_name my value with spaces +ignore-unknown-option anopt +ignore-unknown-option anopt anotheropt +script-security 3 +disable-occ +user username +group groupname +cd /canonical/dir +cd relative/dir/ +chroot /canonical/dir +chroot relative/dir/ +setcon selinux-context +daemon +daemon mydaemon_name +syslog +syslog my_syslog-name +errors-to-stderr +passtos +inetd +inetd wait +inetd nowait +inetd wait my-program_name +log myfilename +log-append myfilename +suppress-timestamps +writepid myfile +nice 5 +fast-io +multihome +echo stuff to echo until end of line +remap-usr1 SIGHUP +remap-usr1 SIGTERM +verb 6 +status myfile +status myfile 15 +status-version +status-version 3 +mute 20 +comp-lzo +comp-lzo yes +comp-lzo no +comp-lzo adaptive +management 123.123.123.123 1234 +management 123.123.123.123 1234 /canonical/file +management-client +management-query-passwords +management-query-proxy +management-query-remote +management-forget-disconnect +management-hold +management-signal +management-up-down +management-client-auth +management-client-pf +management-log-cache 5 +management-client-user myuser +management-client-user mygroup +plugin /canonical/file +plugin relative/file +plugin myfile an init string +server 1.2.3.4 255.255.255.0 +server 1.2.3.4 255.255.255.255 nopool +server-bridge 1.2.3.4 1.2.3.5 50.5.5.5 50.5.5.6 +server-bridge nogw +push \"my push string\" +push-reset +push-peer-info +disable +ifconfig-pool 1.1.1.1 2.2.2.2 +ifconfig-pool 1.1.1.1 2.2.2.2 255.255.255.0 +ifconfig-pool-persist myfile +ifconfig-pool-persist myfile 50 +ifconfig-pool-linear +ifconfig-push 1.1.1.1 2.2.2.2 +ifconfig-push 1.1.1.1 2.2.2.2 alias-name +iroute 1.1.1.1 +iroute 1.1.1.1 2.2.2.2 +client-to-client +duplicate-cn +client-connect my command goes here +client-disconnect my command goes here +client-config-dir directory +ccd-exclusive +tmp-dir /directory +hash-size 1 2 +bcast-buffers 5 +tcp-queue-limit 50 +tcp-nodelay +max-clients 50 +max-routes-per-client 50 +stale-routes-check 5 +stale-routes-check 5 50 +connect-freq 50 100 +learn-address my command goes here +auth-user-pass-verify /my/script/with/no/arguments.sh via-env +auth-user-pass-verify \"myscript.sh arg1 arg2\" via-file +opt-verify +auth-user-pass-optional +client-cert-not-required +username-as-common-name +port-share 1.1.1.1 1234 +port-share myhostname 1234 +port-share myhostname 1234 /canonical/dir +client +pull +auth-user-pass +auth-user-pass /canonical/file +auth-user-pass relative/file +auth-retry none +auth-retry nointeract +auth-retry interact +static-challenge challenge_no_spaces 1 +static-challenge \"my quoted challenge string\" 0 +server-poll-timeout 50 +explicit-exit-notify +explicit-exit-notify 5 +secret /canonicalfile +secret relativefile +secret filename 1 +secret filename 0 +key-direction +auth none +auth sha1 +cipher SHA1 +cipher sha1 +keysize 50 +prng SHA1 +prng SHA1 500 +engine +engine blah +no-replay +replay-window 64 +replay-window 64 16 +mute-replay-warnings +replay-persist /my/canonical/filename +no-iv +use-prediction-resistance +test-crypto +tls-server +tls-client +ca myfile +capath /mydir/ +dh myfile +cert myfile +extra-certs myfile +key myfile +tls-version-min 1.1 +tls-version-min 2 +tls-version-min 1.1 or-highest +tls-version-max 5.5 +pkcs12 myfile +verify-hash AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16 +pkcs11-cert-private 0 +pkcs11-cert-private 1 +pkcs11-id myname +pkcs11-id-management +pkcs11-pin-cache 50 +pkcs11-protected-authentication 0 +pkcs11-protected-authentication 1 +cryptoapicert \"SUBJ:Justin Akers\" +key-method 2 +tls-cipher DEFAULT:!EXP:!PSK:!SRP:!kRSA +tls-timeout 50 +reneg-bytes 50 +reneg-pkts 50 +reneg-sec 5 +hand-window 123 +tran-window 456 +single-session +tls-exit +tls-auth filename 1 +askpass /canonical/filename +auth-nocache +tls-verify my command goes here +tls-export-cert /a/directory/for/things +x509-username-field emailAddress +x509-username-field ext:subjectAltName +tls-remote myhostname +verify-x509-name hostname name +verify-x509-name hostname name-prefix +verify-x509-name hostname subject +ns-cert-type server +ns-cert-type client +remote-cert-tls server +remote-cert-tls client +remote-cert-ku 01 +remote-cert-ku 01 02 fa FF b3 +remote-cert-eku 123.3510.350.10 +remote-cert-eku \"TLS Web Client Authentication\" +remote-cert-eku serverAuth +crl-verify /a/file/path +crl-verify /a/directory/ dir +show-ciphers +show-digests +show-tls +show-engines +genkey +mktun +rmtun +ifconfig-ipv6 2000:123:456::/64 1234:99:123::124 +ifconfig-ipv6-push 2000:123:456::/64 1234:99:123::124 +iroute-ipv6 2000:123:456::/64 +route-ipv6 2000:123:456::/64 +route-ipv6 2000:123:456::/64 1234:99:123::124 +route-ipv6 2000:123:456::/64 1234:99:123::124 500 +server-ipv6 2000:123:456::/64 +ifconfig-ipv6-pool 2000:123:456::/64 + +" + +test OpenVPN.lns get all_permutations_conf = + { } + { "config" = "/a/canonical/file" } + { "config" = "relative_file" } + { "mode" = "p2p" } + { "mode" = "server" } + { "local" = "192.168.1.1" } + { "local" = "hostname" } + { "remote" + { "server" = "192.168.1.1" } + { "port" = "1234" } + } + { "remote" + { "server" = "hostname" } + { "port" = "1234" } + } + { "remote" + { "server" = "hostname" } + } + { "remote" + { "server" = "192.168.1.1" } + } + { "remote" + { "server" = "hostname" } + { "port" = "1234" } + { "proto" = "tcp" } + } + { "remote" + { "server" = "192.168.1.1" } + { "port" = "1234" } + { "proto" = "tcp" } + } + { "remote" + { "server" = "hostname" } + { "port" = "1234" } + { "proto" = "udp" } + } + { "remote-random-hostname" } + { "#comment" = "comment square blocks should go here" } + { "proto-force" = "udp" } + { "proto-force" = "tcp" } + { "remote-random" } + { "proto" = "udp" } + { "proto" = "tcp-client" } + { "proto" = "tcp-server" } + { "connect-retry" = "5" } + { "connect-timeout" = "10" } + { "connect-retry-max" = "0" } + { "show-proxy-settings" } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "auto" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "auto-nct" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "auto" } + { "auth-method" = "none" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "auto" } + { "auth-method" = "basic" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "auto" } + { "auth-method" = "ntlm" } + } { "http-proxy" - { "server" = "mytest2" } } - { "http-proxy" } + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "relative_filename" } + { "auth-method" = "ntlm" } + } + { "http-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "/canonical/filename" } + { "auth-method" = "basic" } + } + { "http-proxy-retry" } + { "http-proxy-timeout" = "5" } + { "http-proxy-option" + { "option" = "VERSION" } + { "value" = "1.0" } + } + { "http-proxy-option" + { "option" = "AGENT" } + { "value" = "an unquoted string with spaces" } + } + { "http-proxy-option" + { "option" = "AGENT" } + { "value" = "an_unquoted_string_without_spaces" } + } + { "socks-proxy" + { "server" = "servername" } + } + { "socks-proxy" + { "server" = "servername" } + { "port" = "1234" } + } + { "socks-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "/canonical/file" } + } + { "socks-proxy" + { "server" = "servername" } + { "port" = "1234" } + { "auth" = "relative/file" } + } + { "socks-proxy-retry" } + { "resolv-retry" = "5" } + { "float" } + { "ipchange" = "my command goes here" } + { "port" = "1234" } + { "lport" = "1234" } + { "rport" = "1234" } + { "bind" } + { "nobind" } + { "dev" = "tun" } + { "dev" = "tun0" } + { "dev" = "tap" } + { "dev" = "tap0" } + { "dev" = "null" } + { "dev-type" = "tun" } + { "dev-type" = "tap" } + { "topology" = "net30" } + { "topology" = "p2p" } + { "topology" = "subnet" } + { "tun-ipv6" } + { "dev-node" = "/canonical/file" } + { "dev-node" = "relative/file" } + { "lladdr" = "1.2.3.4" } + { "iproute" = "my command goes here" } + { "ifconfig" + { "local" = "1.2.3.4" } + { "remote" = "5.6.7.8" } + } + { "ifconfig-noexec" } + { "ifconfig-nowarn" } + { "route" + { "address" = "111.222.123.123" } + } + { "route" + { "address" = "networkname" } + } + { "route" + { "address" = "vpn_gateway" } + } + { "route" + { "address" = "net_gateway" } + } + { "route" + { "address" = "remote_host" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.221" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "default" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "111.222.123.1" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "default" } + { "gateway" = "111.222.123.1" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "default" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "default" } + { "gateway" = "default" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "gatewayname" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "gatewayname" } + { "metric" = "5" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "vpn_gateway" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "net_gateway" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "remote_host" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "111.222.123.1" } + } + { "route" + { "address" = "111.222.123.123" } + { "netmask" = "255.123.255.231" } + { "gateway" = "111.222.123.1" } + { "metric" = "5" } + } + { "max-routes" = "5" } + { "route-gateway" = "gateway-name" } + { "route-gateway" = "111.222.123.1" } + { "route-gateway" = "dhcp" } + { "route-metric" = "5" } + { "route-delay" } + { "route-delay" + { "seconds" = "1" } + } + { "route-delay" + { "seconds" = "1" } + { "win-seconds" = "2" } + } + { "route-up" = "my command goes here" } + { "route-pre-down" = "my command goes here" } + { "route-noexec" } + { "route-nopull" } + { "allow-pull-fqdn" } + { "client-nat" + { "type" = "snat" } + { "network" = "1.2.3.4" } + { "netmask" = "5.6.7.8" } + { "alias" = "9.8.7.6" } + } + { "client-nat" + { "type" = "dnat" } + { "network" = "1.2.3.4" } + { "netmask" = "5.6.7.8" } + { "alias" = "9.8.7.6" } + } + { "redirect-gateway" + { "flag" = "local" } + } + { "redirect-gateway" + { "flag" = "local" } + { "flag" = "autolocal" } + } + { "redirect-gateway" + { "flag" = "local" } + { "flag" = "autolocal" } + { "flag" = "def1" } + { "flag" = "bypass-dhcp" } + { "flag" = "bypass-dns" } + { "flag" = "block-local" } + } + { "link-mtu" = "5" } + { "redirect-private" + { "flag" = "local" } + } + { "redirect-private" + { "flag" = "local" } + { "flag" = "autolocal" } + } + { "redirect-private" + { "flag" = "local" } + { "flag" = "autolocal" } + { "flag" = "def1" } + { "flag" = "bypass-dhcp" } + { "flag" = "bypass-dns" } + { "flag" = "block-local" } + } + { "tun-mtu" = "5" } + { "tun-mtu-extra" = "5" } + { "mtu-disc" = "no" } + { "mtu-disc" = "maybe" } + { "mtu-disc" = "yes" } + { "mtu-test" } + { "fragment" = "5" } + { "mssfix" = "1600" } + { "sndbuf" = "65536" } + { "rcvbuf" = "65535" } + { "mark" = "blahvalue" } + { "socket-flags" = "TCP_NODELAY" } + { "txqueuelen" = "5" } + { "shaper" = "50" } + { "inactive" + { "seconds" = "5" } + } + { "inactive" + { "seconds" = "5" } + { "bytes" = "1024" } + } + { "ping" = "10" } + { "ping-exit" = "10" } + { "ping-restart" = "10" } + { "keepalive" + { "ping" = "1" } + { "timeout" = "2" } + } + { "ping-timer-rem" } + { "persist-tun" } + { "persist-key" } + { "persist-local-ip" } + { "persist-remote-ip" } + { "mlock" } + { "up" = "my command goes here" } + { "up-delay" } + { "down" = "my command goes here" } + { "down-pre" } + { "up-restart" } + { "setenv" + { "myname" = "myvalue" } + } + { "setenv" + { "my0-_name" = "my value with spaces" } + } + { "setenv-safe" + { "myname" = "myvalue" } + } + { "setenv-safe" + { "my-_name" = "my value with spaces" } + } + { "ignore-unknown-option" + { "opt" = "anopt" } + } + { "ignore-unknown-option" + { "opt" = "anopt" } + { "opt" = "anotheropt" } + } + { "script-security" = "3" } + { "disable-occ" } + { "user" = "username" } + { "group" = "groupname" } + { "cd" = "/canonical/dir" } + { "cd" = "relative/dir/" } + { "chroot" = "/canonical/dir" } + { "chroot" = "relative/dir/" } + { "setcon" = "selinux-context" } + { "daemon" } + { "daemon" = "mydaemon_name" } + { "syslog" } + { "syslog" = "my_syslog-name" } + { "errors-to-stderr" } + { "passtos" } + { "inetd" } + { "inetd" + { "mode" = "wait" } + } + { "inetd" + { "mode" = "nowait" } + } + { "inetd" + { "mode" = "wait" } + { "progname" = "my-program_name" } + } + { "log" = "myfilename" } + { "log-append" = "myfilename" } + { "suppress-timestamps" } + { "writepid" = "myfile" } + { "nice" = "5" } + { "fast-io" } + { "multihome" } + { "echo" = "stuff to echo until end of line" } + { "remap-usr1" = "SIGHUP" } + { "remap-usr1" = "SIGTERM" } + { "verb" = "6" } + { "status" + { "file" = "myfile" } + } + { "status" + { "file" = "myfile" } + { "repeat-seconds" = "15" } + } + { "status-version" } + { "status-version" = "3" } + { "mute" = "20" } + { "comp-lzo" } + { "comp-lzo" = "yes" } + { "comp-lzo" = "no" } + { "comp-lzo" = "adaptive" } + { "management" + { "server" = "123.123.123.123" } + { "port" = "1234" } + } + { "management" + { "server" = "123.123.123.123" } + { "port" = "1234" } + { "pwfile" = "/canonical/file" } + } + { "management-client" } + { "management-query-passwords" } + { "management-query-proxy" } + { "management-query-remote" } + { "management-forget-disconnect" } + { "management-hold" } + { "management-signal" } + { "management-up-down" } + { "management-client-auth" } + { "management-client-pf" } + { "management-log-cache" = "5" } + { "management-client-user" = "myuser" } + { "management-client-user" = "mygroup" } + { "plugin" + { "file" = "/canonical/file" } + } + { "plugin" + { "file" = "relative/file" } + } + { "plugin" + { "file" = "myfile" } + { "init-string" = "an init string" } + } + { "server" + { "address" = "1.2.3.4" } + { "netmask" = "255.255.255.0" } + } + { "server" + { "address" = "1.2.3.4" } + { "netmask" = "255.255.255.255" } + { "nopool" } + } + { "server-bridge" + { "address" = "1.2.3.4" } + { "netmask" = "1.2.3.5" } + { "start" = "50.5.5.5" } + { "end" = "50.5.5.6" } + } + { "server-bridge" = "nogw" } + { "push" = "my push string" } + { "push-reset" } + { "push-peer-info" } + { "disable" } + { "ifconfig-pool" + { "start" = "1.1.1.1" } + { "end" = "2.2.2.2" } + } + { "ifconfig-pool" + { "start" = "1.1.1.1" } + { "end" = "2.2.2.2" } + { "netmask" = "255.255.255.0" } + } + { "ifconfig-pool-persist" + { "file" = "myfile" } + } + { "ifconfig-pool-persist" + { "file" = "myfile" } + { "seconds" = "50" } + } + { "ifconfig-pool-linear" } + { "ifconfig-push" + { "local" = "1.1.1.1" } + { "remote-netmask" = "2.2.2.2" } + } + { "ifconfig-push" + { "local" = "1.1.1.1" } + { "remote-netmask" = "2.2.2.2" } + { "alias" = "alias-name" } + } + { "iroute" + { "local" = "1.1.1.1" } + } + { "iroute" + { "local" = "1.1.1.1" } + { "netmask" = "2.2.2.2" } + } + { "client-to-client" } + { "duplicate-cn" } + { "client-connect" = "my command goes here" } + { "client-disconnect" = "my command goes here" } + { "client-config-dir" = "directory" } + { "ccd-exclusive" } + { "tmp-dir" = "/directory" } + { "hash-size" + { "real" = "1" } + { "virtual" = "2" } + } + { "bcast-buffers" = "5" } + { "tcp-queue-limit" = "50" } + { "tcp-nodelay" } + { "max-clients" = "50" } + { "max-routes-per-client" = "50" } + { "stale-routes-check" + { "age" = "5" } + } + { "stale-routes-check" + { "age" = "5" } + { "interval" = "50" } + } + { "connect-freq" + { "num" = "50" } + { "sec" = "100" } + } + { "learn-address" = "my command goes here" } + { "auth-user-pass-verify" + { + { "command" = "/my/script/with/no/arguments.sh" } + } + { "method" = "via-env" } + } + { "auth-user-pass-verify" + { + { "command" = "myscript.sh arg1 arg2" } + } + { "method" = "via-file" } + } + { "opt-verify" } + { "auth-user-pass-optional" } + { "client-cert-not-required" } + { "username-as-common-name" } + { "port-share" + { "host" = "1.1.1.1" } + { "port" = "1234" } + } + { "port-share" + { "host" = "myhostname" } + { "port" = "1234" } + } + { "port-share" + { "host" = "myhostname" } + { "port" = "1234" } + { "dir" = "/canonical/dir" } + } + { "client" } + { "pull" } + { "auth-user-pass" } + { "auth-user-pass" = "/canonical/file" } + { "auth-user-pass" = "relative/file" } + { "auth-retry" = "none" } + { "auth-retry" = "nointeract" } + { "auth-retry" = "interact" } + { "static-challenge" + { + { "text" = "challenge_no_spaces" } + } + { "echo" = "1" } + } + { "static-challenge" + { + { "text" = "my quoted challenge string" } + } + { "echo" = "0" } + } + { "server-poll-timeout" = "50" } + { "explicit-exit-notify" } + { "explicit-exit-notify" = "5" } + { "secret" + { "file" = "/canonicalfile" } + } + { "secret" + { "file" = "relativefile" } + } + { "secret" + { "file" = "filename" } + { "direction" = "1" } + } + { "secret" + { "file" = "filename" } + { "direction" = "0" } + } + { "key-direction" } + { "auth" = "none" } + { "auth" = "sha1" } + { "cipher" = "SHA1" } + { "cipher" = "sha1" } + { "keysize" = "50" } + { "prng" + { "algorithm" = "SHA1" } + } + { "prng" + { "algorithm" = "SHA1" } + { "nsl" = "500" } + } + { "engine" } + { "engine" = "blah" } + { "no-replay" } + { "replay-window" + { "window-size" = "64" } + } + { "replay-window" + { "window-size" = "64" } + { "seconds" = "16" } + } { "mute-replay-warnings" } + { "replay-persist" = "/my/canonical/filename" } + { "no-iv" } + { "use-prediction-resistance" } + { "test-crypto" } + { "tls-server" } + { "tls-client" } + { "ca" = "myfile" } + { "capath" = "/mydir/" } + { "dh" = "myfile" } + { "cert" = "myfile" } + { "extra-certs" = "myfile" } + { "key" = "myfile" } + { "tls-version-min" = "1.1" } + { "tls-version-min" = "2" } + { "tls-version-min" = "1.1" + { "or-highest" } + } + { "tls-version-max" = "5.5" } + { "pkcs12" = "myfile" } + { "verify-hash" = "AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16" } + { "pkcs11-cert-private" = "0" } + { "pkcs11-cert-private" = "1" } + { "pkcs11-id" = "myname" } + { "pkcs11-id-management" } + { "pkcs11-pin-cache" = "50" } + { "pkcs11-protected-authentication" = "0" } + { "pkcs11-protected-authentication" = "1" } + { "cryptoapicert" + { "SUBJ" = "Justin Akers" } + } + { "key-method" = "2" } + { "tls-cipher" + { "cipher" = "DEFAULT" } + { "cipher" = "!EXP" } + { "cipher" = "!PSK" } + { "cipher" = "!SRP" } + { "cipher" = "!kRSA" } + } + { "tls-timeout" = "50" } + { "reneg-bytes" = "50" } + { "reneg-pkts" = "50" } + { "reneg-sec" = "5" } + { "hand-window" = "123" } + { "tran-window" = "456" } + { "single-session" } + { "tls-exit" } + { "tls-auth" + { "key" = "filename" } + { "is_client" = "1" } + } + { "askpass" = "/canonical/filename" } + { "auth-nocache" } + { "tls-verify" = "my command goes here" } + { "tls-export-cert" = "/a/directory/for/things" } + { "x509-username-field" + { "subj" = "emailAddress" } + } + { "x509-username-field" + { "ext" = "subjectAltName" } + } + { "tls-remote" = "myhostname" } + { "verify-x509-name" + { "name" = "hostname" } + { "type" = "name" } + } + { "verify-x509-name" + { "name" = "hostname" } + { "type" = "name-prefix" } + } + { "verify-x509-name" + { "name" = "hostname" } + { "type" = "subject" } + } { "ns-cert-type" = "server" } - { "mssfix" = "1350" } + { "ns-cert-type" = "client" } + { "remote-cert-tls" = "server" } + { "remote-cert-tls" = "client" } + { "remote-cert-ku" + { "usage" = "01" } + } + { "remote-cert-ku" + { "usage" = "01" } + { "usage" = "02" } + { "usage" = "fa" } + { "usage" = "FF" } + { "usage" = "b3" } + } + { "remote-cert-eku" + { "oid" = "123.3510.350.10" } + } + { "remote-cert-eku" + { "symbol" = "TLS Web Client Authentication" } + } + { "remote-cert-eku" + { "symbol" = "serverAuth" } + } + { "crl-verify" = "/a/file/path" } + { "crl-verify" = "/a/directory/" + { "dir" } + } + { "show-ciphers" } + { "show-digests" } + { "show-tls" } + { "show-engines" } + { "genkey" } + { "mktun" } + { "rmtun" } + { "ifconfig-ipv6" + { "address" = "2000:123:456::/64" } + { "remote" = "1234:99:123::124" } + } + { "ifconfig-ipv6-push" + { "address" = "2000:123:456::/64" } + { "remote" = "1234:99:123::124" } + } + { "iroute-ipv6" = "2000:123:456::/64" } + { "route-ipv6" + { "network" = "2000:123:456::/64" } + } + { "route-ipv6" + { "network" = "2000:123:456::/64" } + { "gateway" = "1234:99:123::124" } + } + { "route-ipv6" + { "network" = "2000:123:456::/64" } + { "gateway" = "1234:99:123::124" } + { "metric" = "500" } + } + { "server-ipv6" = "2000:123:456::/64" } + { "ifconfig-ipv6-pool" = "2000:123:456::/64" } + { } diff --git a/lenses/tests/test_properties.aug b/lenses/tests/test_properties.aug index 24c1fc6..5901fbf 100644 --- a/lenses/tests/test_properties.aug +++ b/lenses/tests/test_properties.aug @@ -1,5 +1,6 @@ module Test_properties = let conf = " +# # Test tomcat properties file #tomcat.commented.value=1 # config @@ -50,7 +51,7 @@ overflow.description=\ let lns = Properties.lns test lns get conf = - { } + { } { } { "#comment" = "Test tomcat properties file" } { "#comment" = "tomcat.commented.value=1" } { "#comment" = "config" } @@ -103,6 +104,7 @@ test lns put conf after set "tomcat.port" "99"; set "tomcat.application.host" "foo.network.com" = " +# # Test tomcat properties file #tomcat.commented.value=1 # config diff --git a/lenses/tests/test_puppetfile.aug b/lenses/tests/test_puppetfile.aug index 8784a3f..6837b2a 100644 --- a/lenses/tests/test_puppetfile.aug +++ b/lenses/tests/test_puppetfile.aug @@ -56,3 +56,10 @@ mod 'herculesteam/augeasproviders', '2.1.x'\n" = { "3" = "herculesteam/augeasproviders" { "@version" = "2.1.x" } } + +(* Test: Puppetfile.lns + Owner is not mandatory if git is given *) +test Puppetfile.lns get "mod 'stdlib', + :git => \"git://github.com/puppetlabs/puppetlabs-stdlib.git\"\n" = + { "1" = "stdlib" + { "git" = "git://github.com/puppetlabs/puppetlabs-stdlib.git" } } diff --git a/lenses/tests/test_rabbitmq.aug b/lenses/tests/test_rabbitmq.aug index 430e846..8a01541 100644 --- a/lenses/tests/test_rabbitmq.aug +++ b/lenses/tests/test_rabbitmq.aug @@ -18,12 +18,17 @@ test Rabbitmq.ssl_options get "{ssl_options, [ {certfile,\"/path/to/server/cert.pem\"}, {keyfile,\"/path/to/server/key.pem\"}, {verify,verify_peer}, + {versions, ['tlsv1.2', 'tlsv1.1', 'tlsv1']}, {fail_if_no_peer_cert,false}]}" = { "ssl_options" { "cacertfile" = "/path/to/testca/cacert.pem" } { "certfile" = "/path/to/server/cert.pem" } { "keyfile" = "/path/to/server/key.pem" } { "verify" = "verify_peer" } + { "versions" + { "value" = "tlsv1.2" } + { "value" = "tlsv1.1" } + { "value" = "tlsv1" } } { "fail_if_no_peer_cert" = "false" } } (* Test: Rabbitmq.disk_free_limit *) @@ -62,6 +67,21 @@ test Rabbitmq.cluster_nodes get "{cluster_nodes, ['rabbit@rabbit1', 'rabbit@rabb { "value" = "rabbit@rabbit2" } { "value" = "rabbit@rabbit3" } } +(* Test: Rabbitmq.cluster_partition_handling, single value *) +test Rabbitmq.cluster_partition_handling get "{cluster_partition_handling, ignore}" = + { "cluster_partition_handling" = "ignore" } + +(* Test: Rabbitmq.cluster_partition_handling, tuple *) +test Rabbitmq.cluster_partition_handling get "{cluster_partition_handling, {pause_if_all_down, ['rabbit@rabbit1', 'rabbit@rabbit2', 'rabbit@rabbit3'], autoheal}}" = + { "cluster_partition_handling" + { "tuple" + { "value" = "pause_if_all_down" } + { "value" + { "value" = "rabbit@rabbit1" } + { "value" = "rabbit@rabbit2" } + { "value" = "rabbit@rabbit3" } } + { "value" = "autoheal" } } } + (* Test: Rabbitmq.lns Top-level test *) test Rabbitmq.lns get " diff --git a/lenses/tests/test_reprepro_uploaders.aug b/lenses/tests/test_reprepro_uploaders.aug index d838f98..14d6c16 100644 --- a/lenses/tests/test_reprepro_uploaders.aug +++ b/lenses/tests/test_reprepro_uploaders.aug @@ -43,6 +43,17 @@ test Reprepro_Uploaders.entry get { "by" = "anybody" } } (* Test: Reprepro_Uploaders.entry + Check the field distribution *) +test Reprepro_Uploaders.entry get + "allow distribution 'sid' by anybody\n" = + + { "allow" + { "and" + { "or" = "distribution" + { "or" = "sid" } } } + { "by" = "anybody" } } + +(* Test: Reprepro_Uploaders.entry Some checks use the "contain" keyword to loosen the condition. In that case, a "contain" subnode is added. Be sure to check for it to know how the condition has to be checked. @@ -122,3 +133,34 @@ test Reprepro_Uploaders.lns get conf = { "or" = "source" { "not" } { "or" = "*melanie*" } { "or" = "katya" } } } { "by" = "key" { "key" = "any" } } } + +(* Test: Reprepro_Uploaders.lns + Support group conditions, GH #283 *) +test Reprepro_Uploaders.lns get "allow sections 'desktop/*' by group groupname\n" = + { "allow" + { "and" { "or" = "sections" { "or" = "desktop/*" } } } + { "by" = "group" { "group" = "groupname" } } } + +(* Test: Reprepro_Uploaders.lns + Declare group condition, GH #283 *) +test Reprepro_Uploaders.lns get "group groupname add key-id\n" = + { "group" = "groupname" + { "add" = "key-id" } } + +(* Test: Reprepro_Uploaders.lns + Group inheritance, GH #283 *) +test Reprepro_Uploaders.lns get "group groupname contains group2\n" = + { "group" = "groupname" + { "contains" = "group2" } } + +(* Test: Reprepro_Uploaders.lns + Empty group, GH #283 *) +test Reprepro_Uploaders.lns get "group groupname empty\n" = + { "group" = "groupname" + { "empty" } } + +(* Test: Reprepro_Uploaders.lns + Unused group, GH #283 *) +test Reprepro_Uploaders.lns get "group groupname unused\n" = + { "group" = "groupname" + { "unused" } } diff --git a/lenses/tests/test_rhsm.aug b/lenses/tests/test_rhsm.aug new file mode 100644 index 0000000..219a5be --- /dev/null +++ b/lenses/tests/test_rhsm.aug @@ -0,0 +1,151 @@ +(* +Module: Test_Rhsm + Provides unit tests and examples for the lens. +*) + +module Test_rhsm = + + (* Variable: conf + A full rhsm.conf *) + let conf = "# Red Hat Subscription Manager Configuration File: + +# Unified Entitlement Platform Configuration +[server] +# Server hostname: +hostname = subscription.rhn.redhat.com + +# Server prefix: +prefix = /subscription + +# Server port: +port = 443 + +# Set to 1 to disable certificate validation: +insecure = 0 + +# Set the depth of certs which should be checked +# when validating a certificate +ssl_verify_depth = 3 + +# an http proxy server to use +proxy_hostname = + +# port for http proxy server +proxy_port = + +# user name for authenticating to an http proxy, if needed +proxy_user = + +# password for basic http proxy auth, if needed +proxy_password = + +[rhsm] +# Content base URL: +baseurl= https://cdn.redhat.com + +# Server CA certificate location: +ca_cert_dir = /etc/rhsm/ca/ + +# Default CA cert to use when generating yum repo configs: +repo_ca_cert = %(ca_cert_dir)sredhat-uep.pem + +# Where the certificates should be stored +productCertDir = /etc/pki/product +entitlementCertDir = /etc/pki/entitlement +consumerCertDir = /etc/pki/consumer + +# Manage generation of yum repositories for subscribed content: +manage_repos = 1 + +# Refresh repo files with server overrides on every yum command +full_refresh_on_yum = 0 + +# If set to zero, the client will not report the package profile to +# the subscription management service. +report_package_profile = 1 + +# The directory to search for subscription manager plugins +pluginDir = /usr/share/rhsm-plugins + +# The directory to search for plugin configuration files +pluginConfDir = /etc/rhsm/pluginconf.d + +[rhsmcertd] +# Interval to run cert check (in minutes): +certCheckInterval = 240 +# Interval to run auto-attach (in minutes): +autoAttachInterval = 1440 +" + + test Rhsm.lns get conf = + { "#comment" = "Red Hat Subscription Manager Configuration File:" } + { } + { "#comment" = "Unified Entitlement Platform Configuration" } + { "server" + { "#comment" = "Server hostname:" } + { "hostname" = "subscription.rhn.redhat.com" } + { } + { "#comment" = "Server prefix:" } + { "prefix" = "/subscription" } + { } + { "#comment" = "Server port:" } + { "port" = "443" } + { } + { "#comment" = "Set to 1 to disable certificate validation:" } + { "insecure" = "0" } + { } + { "#comment" = "Set the depth of certs which should be checked" } + { "#comment" = "when validating a certificate" } + { "ssl_verify_depth" = "3" } + { } + { "#comment" = "an http proxy server to use" } + { "proxy_hostname" } + { } + { "#comment" = "port for http proxy server" } + { "proxy_port" } + { } + { "#comment" = "user name for authenticating to an http proxy, if needed" } + { "proxy_user" } + { } + { "#comment" = "password for basic http proxy auth, if needed" } + { "proxy_password" } + { } + } + { "rhsm" + { "#comment" = "Content base URL:" } + { "baseurl" = "https://cdn.redhat.com" } + { } + { "#comment" = "Server CA certificate location:" } + { "ca_cert_dir" = "/etc/rhsm/ca/" } + { } + { "#comment" = "Default CA cert to use when generating yum repo configs:" } + { "repo_ca_cert" = "%(ca_cert_dir)sredhat-uep.pem" } + { } + { "#comment" = "Where the certificates should be stored" } + { "productCertDir" = "/etc/pki/product" } + { "entitlementCertDir" = "/etc/pki/entitlement" } + { "consumerCertDir" = "/etc/pki/consumer" } + { } + { "#comment" = "Manage generation of yum repositories for subscribed content:" } + { "manage_repos" = "1" } + { } + { "#comment" = "Refresh repo files with server overrides on every yum command" } + { "full_refresh_on_yum" = "0" } + { } + { "#comment" = "If set to zero, the client will not report the package profile to" } + { "#comment" = "the subscription management service." } + { "report_package_profile" = "1" } + { } + { "#comment" = "The directory to search for subscription manager plugins" } + { "pluginDir" = "/usr/share/rhsm-plugins" } + { } + { "#comment" = "The directory to search for plugin configuration files" } + { "pluginConfDir" = "/etc/rhsm/pluginconf.d" } + { } + } + { "rhsmcertd" + { "#comment" = "Interval to run cert check (in minutes):" } + { "certCheckInterval" = "240" } + { "#comment" = "Interval to run auto-attach (in minutes):" } + { "autoAttachInterval" = "1440" } + } diff --git a/lenses/tests/test_rsyslog.aug b/lenses/tests/test_rsyslog.aug index e696139..6fcc6f5 100644 --- a/lenses/tests/test_rsyslog.aug +++ b/lenses/tests/test_rsyslog.aug @@ -23,6 +23,7 @@ authpriv.* /var/log/secure *.* @@2.7.4.1 *.emerg :omusrmsg:* *.emerg :omusrmsg:foo,bar +*.emerg | /dev/xconsole " (* Test: Rsyslog.lns *) @@ -118,6 +119,15 @@ test Rsyslog.lns get conf = { "omusrmsg" = "foo" } { "omusrmsg" = "bar" } } } + { "entry" + { "selector" + { "facility" = "*" } + { "level" = "emerg" } + } + { "action" + { "pipe" = "/dev/xconsole" } + } + } (* Parse complex $template lines, RHBZ#1083016 *) test Rsyslog.lns get "$template SpiceTmpl,\"%TIMESTAMP%.%TIMESTAMP:::date-subseconds% %syslogtag% %syslogseverity-text%:%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\\n\"\n" = @@ -140,3 +150,11 @@ test Rsyslog.lns get ":msg, !contains, \"error\" /var/log/noterror.log\n" = { "value" = "error" } { "action" { "file" = "/var/log/noterror.log" } } } + +test Rsyslog.lns get ":msg,!contains,\"garbage\" ~\n" = + { "filter" + { "property" = "msg" } + { "operation" = "!contains" } + { "value" = "garbage" } + { "action" + { "discard" } } } diff --git a/lenses/tests/test_shellvars.aug b/lenses/tests/test_shellvars.aug index 7a89f11..e81c7f5 100644 --- a/lenses/tests/test_shellvars.aug +++ b/lenses/tests/test_shellvars.aug @@ -165,6 +165,11 @@ unset ONBOOT # We do not want this var { "@builtin" = "exit" } { "@builtin" = "exit" { "args" = "2" } } + (* Allow wrapping builtin arguments to multiple lines *) + test Shellvars.lns get "ulimit -c \\\nunlimited\nulimit \\\n -x 123\n" = + { "@builtin" = "ulimit" { "args" = "-c \\\nunlimited" } } + { "@builtin" = "ulimit" { "args" = "-x 123" } } + (* Test semicolons *) test lns get "VAR1=\"this;is;a;test\"\nVAR2=this;\n" = { "VAR1" = "\"this;is;a;test\"" } @@ -256,11 +261,14 @@ done\n" = ;; esac\n" = { "@case" = "$f" - { "@case_entry" = "/tmp/file1" + { "@case_entry" + { "@pattern" = "/tmp/file1" } { ".source" = "/tmp/file1" } } - { "@case_entry" = "/tmp/file2" + { "@case_entry" + { "@pattern" = "/tmp/file2" } { ".source" = "/tmp/file2" } } - { "@case_entry" = "*" + { "@case_entry" + { "@pattern" = "*" } { "@unset" { "1" = "f" } } } } @@ -303,7 +311,8 @@ esac\n" = esac\n" = { "@case" = "$f" - { "@case_entry" = "a" + { "@case_entry" + { "@pattern" = "a" } { "B" = "C" } } } @@ -320,9 +329,11 @@ esac\n" = ;; esac\n" = { "@case" = "$f" - { "@case_entry" = "a" + { "@case_entry" + { "@pattern" = "a" } { "B" = "C" } } - { "@case_entry" = "b" + { "@case_entry" + { "@pattern" = "b" } { "A" = "D" } } } @@ -343,11 +354,13 @@ unset f esac\n" = { "@case" = "${INTERFACE}" { "#comment" = "comment before" } - { "@case_entry" = "eth0" + { "@case_entry" + { "@pattern" = "eth0" } { "#comment" = "comment in" } { "OPTIONS" = "()" } } { "#comment" = "comment before 2" } - { "@case_entry" = "*" + { "@case_entry" + { "@pattern" = "*" } { "#comment" = "comment in 2" } { "@unset" { "1" = "f" } } } @@ -359,7 +372,7 @@ esac\n" = ;; esac\n" = { "@case" = "$a" - { "@case_entry" = "*" } } + { "@case_entry" { "@pattern" = "*" } } } (* case variables can be surrounded by double quotes *) test Shellvars.lns get "case \"${options}\" in @@ -368,7 +381,8 @@ esac\n" = ;; esac\n" = { "@case" = "\"${options}\"" - { "@case_entry" = "*debug*" + { "@case_entry" + { "@pattern" = "*debug*" } { "@builtin" = "shift" } } } (* Double quoted values can have newlines *) @@ -399,6 +413,10 @@ esac\n" = test Shellvars.lns get "FOO=``bar``\n" = { "FOO" = "``bar``" } + (* Partial quoting is allowed *) + test Shellvars.lns get "FOO=\"$bar\"/'baz'/$(quux)$((1 + 2))\n" = + { "FOO" = "\"$bar\"/'baz'/$(quux)$((1 + 2))" } + (* unset can be used on wildcard variables *) test Shellvars.lns get "unset ${!LC_*}\n" = { "@unset" @@ -464,9 +482,11 @@ test2\"\n" = 1) TestVar=\"test1\" ;; esac\n" = { "@case" = "$ARG" - { "@case_entry" = "0" + { "@case_entry" + { "@pattern" = "0" } { "TestVar" = "\"test0\"" } } - { "@case_entry" = "1" + { "@case_entry" + { "@pattern" = "1" } { "TestVar" = "\"test1\"" } } } (* case: support ;; on the same line with multiple commands *) @@ -477,11 +497,13 @@ esac\n" = Bar=3; Baz=4;; esac\n" = { "@case" = "$ARG" - { "@case_entry" = "0" + { "@case_entry" + { "@pattern" = "0" } { "Foo" = "0" } { "Bar" = "1" } } - { "@case_entry" = "1" + { "@case_entry" + { "@pattern" = "1" } { "Foo" = "2" } { "Bar" = "3" } { "Baz" = "4" } @@ -536,6 +558,193 @@ fi\n" = test lns get "var[alpha_beta,gamma]=something\n" = { "var[alpha_beta,gamma]" = "something" } + (* GH #188: support more conditions *) + test Shellvars.lns get "[ -f $FILENAME ]\n" = + { "@condition" = "-f $FILENAME" + { "type" = "[" } } + + test Shellvars.lns get "[[ -f $FILENAME ]]\n" = + { "@condition" = "-f $FILENAME" + { "type" = "[[" } } + + (* Allow wrapping loop condition to multiple lines *) + test Shellvars.lns get "for x in foo \\\nbar\\\nbaz; do y=$x; done\n" = + { "@for" = "x in foo \\\nbar\\\nbaz" { "y" = "$x" } } + + (* Allow quotes in loop conditions *) + test Shellvars.lns get "for x in \"$@\"; do y=$x; done\n" = + { "@for" = "x in \"$@\"" { "y" = "$x" } } + + (* case: support quotes and spaces in pattern lists *) + test lns get "case $ARG in + \"foo bar\") + Foo=0 + ;; + baz | quux) + Foo=1 + ;; +esac\n" = + { "@case" = "$ARG" + { "@case_entry" + { "@pattern" = "\"foo bar\"" } + { "Foo" = "0" } + } + { "@case_entry" + { "@pattern" = "baz" } + { "@pattern" = "quux" } + { "Foo" = "1" } + } + } + + (* eval *) + test lns get "eval `dircolors`\n" = + { "@eval" = "`dircolors`" } + + (* alias *) + test lns get "alias ls='ls $LS_OPTIONS'\n" = + { "@alias" = "ls" { "value" = "'ls $LS_OPTIONS'" } } + + (* Allow && and || constructs after condition *) + test Shellvars.lns get "[ -f $FILENAME ] && do this || or that\n" = + { "@condition" = "-f $FILENAME" + { "type" = "[" } + { "@and" = "do this" } + { "@or" = "or that" } } + +(* Test: Shellvars.lns + Parse (almost) any command *) +test Shellvars.lns get "echo foobar 'and this is baz' +/usr/local/bin/myscript.sh with args +echo foo \ +bar\n" = + { "@command" = "echo" + { "@arg" = "foobar 'and this is baz'" } + } + { "@command" = "/usr/local/bin/myscript.sh" + { "@arg" = "with args" } + } + { "@command" = "echo" + { "@arg" = "foo \\\nbar" } + } + +(* Test: Shellvars.lns + Support pipes in commands *) +test Shellvars.lns get "echo \"$STRING\" | grep foo\n" = + { "@command" = "echo" + { "@arg" = "\"$STRING\"" } + { "@pipe" + { "@command" = "grep" + { "@arg" = "foo" } } } } + +(* Test: Shellvars.lns + Support && and || after command + GH #215 *) +test Shellvars.lns get "grep -q \"Debian\" /etc/issue && echo moo\n" = + { "@command" = "grep" + { "@arg" = "-q \"Debian\" /etc/issue" } + { "@and" + { "@command" = "echo" + { "@arg" = "moo" } } } } + +test Shellvars.lns get "grep -q \"Debian\" /etc/issue || echo baa\n" = + { "@command" = "grep" + { "@arg" = "-q \"Debian\" /etc/issue" } + { "@or" + { "@command" = "echo" + { "@arg" = "baa" } } } } + +test Shellvars.lns get "grep -q \"Debian\" /etc/issue && DEBIAN=1\n" = + { "@command" = "grep" + { "@arg" = "-q \"Debian\" /etc/issue" } + { "@and" + { "DEBIAN" = "1" } } } + +test Shellvars.lns get "cat /etc/issue | grep -q \"Debian\" && echo moo || echo baa\n" = + { "@command" = "cat" + { "@arg" = "/etc/issue" } + { "@pipe" + { "@command" = "grep" + { "@arg" = "-q \"Debian\"" } + { "@and" + { "@command" = "echo" + { "@arg" = "moo" } + { "@or" + { "@command" = "echo" + { "@arg" = "baa" } } } } } } } } + +(* Command-specific environment variables *) +test Shellvars.lns get "abc=def \\\n ghi=\"jkl mno\" command arg1 arg2\n" = + { "@command" = "command" + { "abc" = "def" } + { "ghi" = "\"jkl mno\"" } + { "@arg" = "arg1 arg2" } + } + +(* Wrapped command sequences *) + +test Shellvars.lns get "foo && \\\nbar baz \\\n|| qux \\\n quux\\\ncorge grault\n" = + { "@command" = "foo" + { "@and" + { "@command" = "bar" + { "@arg" = "baz" } + { "@or" { "@command" = "qux" { "@arg" = "quux\\\ncorge grault" } } } + } + } + } + +(* Comment after function definition (Issue #339) *) +test Shellvars.lns get "SetDir() # hello +{ + echo +}\n" = + { "@function" = "SetDir" + { "#comment" = "hello" } + { "@command" = "echo" } + } + +(* Function with new lines *) +test Shellvars.lns get "MyFunc() +{ + echo +}\n" = + { "@function" = "MyFunc" + { "@command" = "echo" } + } + +(* Pipe and newline without cl (Issue #339) *) +test Shellvars.lns get "echo | +tr\n" = + { "@command" = "echo" + { "@pipe" + { "@command" = "tr" } } } + + +(* Subshell (Issue #339) *) +test Shellvars.lns get "{ echo +}\n" = + { "@subshell" + { "@command" = "echo" } + } + +(* One-liner function *) +test Shellvars.lns get "MyFunc() { echo; }\n" = + { "@function" = "MyFunc" + { "@command" = "echo" } + } + + +(********************************************************* + * Group: Unsupported syntax * + * * + * The following tests are known to be failing currently * + *********************************************************) + +(* Any piping (Issue #343) *) +test Shellvars.lns get "FOO=bar && BAR=foo +echo foo || { echo bar; } +echo FOO | myfunc() { echo bar; }\n" = * + + (* Local Variables: *) (* mode: caml *) (* End: *) diff --git a/lenses/tests/test_smbusers.aug b/lenses/tests/test_smbusers.aug index a4c8371..167e88b 100644 --- a/lenses/tests/test_smbusers.aug +++ b/lenses/tests/test_smbusers.aug @@ -12,6 +12,7 @@ jarwin = JosephArwin manderso = MarkAnderson MarkusAndersonus users = @account nobody = * +;commented = SomeOne " (* Test: Simplevars.lns *) @@ -27,3 +28,4 @@ test SmbUsers.lns get conf = { "username" = "@account" } } { "nobody" { "username" = "*" } } + { "#comment" = "commented = SomeOne" } diff --git a/lenses/tests/test_spacevars.aug b/lenses/tests/test_spacevars.aug index 14b230d..5d02296 100644 --- a/lenses/tests/test_spacevars.aug +++ b/lenses/tests/test_spacevars.aug @@ -6,7 +6,7 @@ keyword value # I like comments very.useful-key my=value - +a.flag " let lns = Spacevars.lns @@ -18,4 +18,4 @@ very.useful-key my=value { "#comment" = "I like comments"} { "very.useful-key" = "my=value" } {} - {} + { "a.flag" } diff --git a/lenses/tests/test_ssh.aug b/lenses/tests/test_ssh.aug index eb78743..b9b865e 100644 --- a/lenses/tests/test_ssh.aug +++ b/lenses/tests/test_ssh.aug @@ -21,6 +21,9 @@ RemoteForward 2221 lhost1:22 LocalForward 3001 remotehost:3000 Ciphers aes128-ctr,aes192-ctr MACs hmac-md5,hmac-sha1,umac-64@openssh.com +HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,ssh-rsa-cert-v01@openssh.com,ssh-rsa +KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256 +PubkeyAcceptedKeyTypes ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,ssh-rsa-cert-v01@openssh.com,ssh-rsa " test Ssh.lns get conf = @@ -61,6 +64,22 @@ MACs hmac-md5,hmac-sha1,umac-64@openssh.com { "2" = "hmac-sha1" } { "3" = "umac-64@openssh.com" } } + { "HostKeyAlgorithms" + { "1" = "ssh-ed25519-cert-v01@openssh.com" } + { "2" = "ssh-ed25519" } + { "3" = "ssh-rsa-cert-v01@openssh.com" } + { "4" = "ssh-rsa" } + } + { "KexAlgorithms" + { "1" = "curve25519-sha256@libssh.org" } + { "2" = "diffie-hellman-group-exchange-sha256" } + } + { "PubkeyAcceptedKeyTypes" + { "1" = "ssh-ed25519-cert-v01@openssh.com" } + { "2" = "ssh-ed25519" } + { "3" = "ssh-rsa-cert-v01@openssh.com" } + { "4" = "ssh-rsa" } + } } (* Test: Ssh.lns @@ -68,3 +87,12 @@ MACs hmac-md5,hmac-sha1,umac-64@openssh.com test Ssh.lns get "Proxycommand ssh -q test nc -q0 %h 22\n" = { "Proxycommand" = "ssh -q test nc -q0 %h 22" } + +(* Test: Ssh.lns + GlobalKnownHostsFile *) +test Ssh.lns get "GlobalKnownHostsFile /etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2\n" = + { "GlobalKnownHostsFile" + { "1" = "/etc/ssh/ssh_known_hosts" } + { "2" = "/etc/ssh/ssh_known_hosts2" } + } + diff --git a/lenses/tests/test_star.aug b/lenses/tests/test_star.aug new file mode 100644 index 0000000..8f0064a --- /dev/null +++ b/lenses/tests/test_star.aug @@ -0,0 +1,63 @@ +module Test_star = + +let conf = "# @(#)star.dfl 1.2 05/08/09 Copyright 2003 J. Schilling +# +# This file is /etc/default/star + +STAR_FIFOSIZE= 32m + +STAR_FIFOSIZE_MAX= 100m + +archive0=/dev/rmt/0 20 0 N +archive1=/dev/rmt/0n 20 0 n +archive2=/dev/rmt/1 20 0 y +archive3=/dev/rmt/1n 20 0 +archive4=/dev/rmt/0 126 0 +archive5=/dev/rmt/0n 126 0 +archive6=/dev/rmt/1 126 0 +archive7=/dev/rmt/1n 126 0 +" +test Star.lns get conf = + { "#comment" = "@(#)star.dfl 1.2 05/08/09 Copyright 2003 J. Schilling" } + { } + { "#comment" = "This file is /etc/default/star" } + { } + { "STAR_FIFOSIZE" = "32m" } + { } + { "STAR_FIFOSIZE_MAX" = "100m" } + { } + { "archive0" + { "device" = "/dev/rmt/0" } + { "block" = "20" } + { "size" = "0" } + { "istape" = "N" } } + { "archive1" + { "device" = "/dev/rmt/0n" } + { "block" = "20" } + { "size" = "0" } + { "istape" = "n" } } + { "archive2" + { "device" = "/dev/rmt/1" } + { "block" = "20" } + { "size" = "0" } + { "istape" = "y" } } + { "archive3" + { "device" = "/dev/rmt/1n" } + { "block" = "20" } + { "size" = "0" } } + { "archive4" + { "device" = "/dev/rmt/0" } + { "block" = "126" } + { "size" = "0" } } + { "archive5" + { "device" = "/dev/rmt/0n" } + { "block" = "126" } + { "size" = "0" } } + { "archive6" + { "device" = "/dev/rmt/1" } + { "block" = "126" } + { "size" = "0" } } + { "archive7" + { "device" = "/dev/rmt/1n" } + { "block" = "126" } + { "size" = "0" } } diff --git a/lenses/tests/test_sudoers.aug b/lenses/tests/test_sudoers.aug index c9f9fff..cc3feb6 100644 --- a/lenses/tests/test_sudoers.aug +++ b/lenses/tests/test_sudoers.aug @@ -183,7 +183,8 @@ www-data +biglab=(rpinson)NOEXEC: ICAL \ { "host_group" { "host" = "ALPHA" } { "command" = "/usr/bin/su [!-]*" } - { "command" = "!/usr/bin/su *root*" } } } + { "command" = "/usr/bin/su *root*" + { "negate" } } } } {} { "spec" { "user" = "@my\ admin\ group" } @@ -326,3 +327,31 @@ test Sudoers.spec get "group+user somehost = ALL\n" = { "command" = "ALL" } } } + +(* Test: Sudoers.spec + GH #262: Sudoers lens doesn't support `!` for command aliases *) +test Sudoers.spec get "%opssudoers ALL=(ALL) ALL, !!!BANNED\n" = + { "spec" + { "user" = "%opssudoers" } + { "host_group" + { "host" = "ALL" } + { "command" = "ALL" + { "runas_user" = "ALL" } } + { "command" = "BANNED" + { "negate" } } + } + } + +(* Test: Sudoers.spec + Handle multiple `!` properly in commands *) +test Sudoers.spec get "%opssudoers ALL=(ALL) ALL, !!!/bin/mount\n" = + { "spec" + { "user" = "%opssudoers" } + { "host_group" + { "host" = "ALL" } + { "command" = "ALL" + { "runas_user" = "ALL" } } + { "command" = "/bin/mount" + { "negate" } } + } + } diff --git a/lenses/tests/test_syslog.aug b/lenses/tests/test_syslog.aug index 2ad1c16..5f97a2b 100644 --- a/lenses/tests/test_syslog.aug +++ b/lenses/tests/test_syslog.aug @@ -212,6 +212,12 @@ daemon.info /var/log/cvsupd.log set "/entry[1]/action/file" "/foo" = "*.* /foo\n" + (* changing file to discard *) + test Syslog.lns put "*.* /var\n" after + rm "/entry[1]/action/file" ; + set "/entry[1]/action/discard" "" + = "*.* ~\n" + (* removing entry *) test Syslog.lns put "*.* /var\n" after rm "/entry[1]" diff --git a/lenses/tests/test_tmpfiles.aug b/lenses/tests/test_tmpfiles.aug new file mode 100644 index 0000000..cc34856 --- /dev/null +++ b/lenses/tests/test_tmpfiles.aug @@ -0,0 +1,359 @@ +(* +Module: Test_Tmpfiles + Provides unit tests and examples for the lens. +*) + +module Test_Tmpfiles = + +(************************************************************************ + * Group: VALID EXAMPLES + *************************************************************************) + (* Variable: simple +One line, simple example *) + let simple = "d /run/user 0755 root mysql 10d -\n" + + (* Variable: simple_tree +Tree for *) + let simple_tree = + { + "1" + { "type" = "d" } + { "path" = "/run/user" } + { "mode" = "0755" } + { "uid" = "root" } + { "gid" = "mysql" } + { "age" = "10d" } + { "argument" = "-" } + } + + (* Variable: complex +A more complex example, comes from the manual *) + let complex = "#Type Path Mode UID GID Age Argument\nd /run/user 0755 root root 10d -\nL /tmp/foobar - - - - /dev/null\n" + + (* Variable: complex_tree +Tree for and *) + let complex_tree = + { "#comment" = "Type Path Mode UID GID Age Argument" } + { "1" + { "type" = "d" } + { "path" = "/run/user" } + { "mode" = "0755" } + { "uid" = "root" } + { "gid" = "root" } + { "age" = "10d" } + { "argument" = "-" } + } + { "2" + { "type" = "L" } + { "path" = "/tmp/foobar" } + { "mode" = "-" } + { "uid" = "-" } + { "gid" = "-" } + { "age" = "-" } + { "argument" = "/dev/null" } + } + + (* Variable: trailing_ws +The complex example with extra spaces *) + let trailing_ws = " #Type Path Mode UID GID Age Argument \n d /run/user 0755 root root 10d - \t\n L /tmp/foobar - - - - /dev/null\t\n" + + (* Variable: empty +Empty example *) + let empty = "\n\n\n" + + (* Variable: exclamation_mark +Example with an exclamation mark in the type *) + let exclamation_mark = "D! /tmp/foo - - - - -\n" + + (* Variable: exclamation_mark_tree +Tree for *) + let exclamation_mark_tree = + { + "1" + { "type" = "D!" } + { "path" = "/tmp/foo" } + { "mode" = "-" } + { "uid" = "-" } + { "gid" = "-" } + { "age" = "-" } + { "argument" = "-" } + } + + (* Variable: short +Example with only type and path *) + let short = "A+ /tmp/foo\n" + + (* Variable: short_tree +Tree for *) + let short_tree = + { + "1" + { "type" = "A+" } + { "path" = "/tmp/foo" } + } + + (* Variable: short_mode +Example with only 3 fields *) + let short_mode = "c+! /tmp/foo ~0755\n" + + (* Variable: short_mode_tree +Tree for *) + let short_mode_tree = + { + "1" + { "type" = "c+!" } + { "path" = "/tmp/foo" } + { "mode" = "~0755" } + } + + (* Variable: short_uid +Example with only 4 fields *) + let short_uid = "A+ /tmp/foo - 0\n" + + (* Variable: short_uid_tree +Tree for *) + let short_uid_tree = + { + "1" + { "type" = "A+" } + { "path" = "/tmp/foo" } + { "mode" = "-" } + { "uid" = "0" } + } + + (* Variable: short_gid +Example with only 5 fields *) + let short_gid = "z /tmp/bar/foo -\t- augd\n" + + (* Variable: short_gid_tree +Tree for *) + let short_gid_tree = + { + "1" + { "type" = "z" } + { "path" = "/tmp/bar/foo" } + { "mode" = "-" } + { "uid" = "-" } + { "gid" = "augd" } + } + + (* Variable: short_age +Example with only 6 fields *) + let short_age = "H /var/tmp/fooBarFOO - jj jj ~10d\n" + + (* Variable: short_age_tree +Tree for *) + let short_age_tree = + { + "1" + { "type" = "H" } + { "path" = "/var/tmp/fooBarFOO" } + { "mode" = "-" } + { "uid" = "jj" } + { "gid" = "jj" } + { "age" = "~10d" } + } + + (* Variable: complex_arg +Complex argument example. That one comes from the manual *) + let complex_arg = "t /run/screen - - - - user.name=\"John Smith\" security.SMACK64=screen\n" + + (* Variable: complex_arg_tree +Tree for *) + let complex_arg_tree = + { + "1" + { "type" = "t" } + { "path" = "/run/screen" } + { "mode" = "-" } + { "uid" = "-" } + { "gid" = "-" } + { "age" = "-" } + { "argument" = "user.name=\"John Smith\" security.SMACK64=screen" } + } + + (* Variable: valid_age +Example with a complex age. *) + let valid_age = "v /var/tmp/js 4221 johnsmith - ~10d12h\n" + + (* Variable: valid_age_tree +Tree for *) + let valid_age_tree = + { + "1" + { "type" = "v" } + { "path" = "/var/tmp/js" } + { "mode" = "4221" } + { "uid" = "johnsmith" } + { "gid" = "-" } + { "age" = "~10d12h" } + } + + (* Variable: valid_second +Example with full age unit *) + let valid_second = "p+ /var/tmp - jsmith - 0second\n" + + (* Variable: valid_second_tree +Tree for *) + let valid_second_tree = + { + "1" + { "type" = "p+" } + { "path" = "/var/tmp" } + { "mode" = "-" } + { "uid" = "jsmith" } + { "gid" = "-" } + { "age" = "0second" } + } + + (* Variable: valid_days +Example with full age unit (plural) *) + let valid_days = "x /var/tmp/manu - jonhsmith - 9days\n" + + (* Variable: valid_days_tree +Tree for *) + let valid_days_tree = + { + "1" + { "type" = "x" } + { "path" = "/var/tmp/manu" } + { "mode" = "-" } + { "uid" = "jonhsmith" } + { "gid" = "-" } + { "age" = "9days" } + } + + (* Variable: percent +Test with a percent sign *) + let percent = "m /var/log/%m 2755 root systemdjournal - -\n" + + (* Variable: percent_tree +Tree for *) + let percent_tree = + { + "1" + { "type" = "m" } + { "path" = "/var/log/%m" } + { "mode" = "2755" } + { "uid" = "root" } + { "gid" = "systemdjournal" } + { "age" = "-" } + { "argument" = "-" } + } + + (* Variable: hyphen +Test with a hyphen in gid *) + let hyphen = "L /var/log/journal 2755 root systemd-journal - -\n" + + (* Variable: hyphen_tree +Tree for *) + let hyphen_tree = + { + "1" + { "type" = "L" } + { "path" = "/var/log/journal" } + { "mode" = "2755" } + { "uid" = "root" } + { "gid" = "systemd-journal" } + { "age" = "-" } + { "argument" = "-" } + } + + (* Variable: valid_base +A valid test to be re-used by the failure cases *) + let valid_base = "H /var/tmp/js 0000 jonhsmith 60 1s foo\n" + + (* Variable: valid_base_tree +Tree for *) + let valid_base_tree = + { + "1" + { "type" = "H" } + { "path" = "/var/tmp/js" } + { "mode" = "0000" } + { "uid" = "jonhsmith" } + { "gid" = "60" } + { "age" = "1s" } + { "argument" = "foo" } + } + +(************************************************************************ + * Group: INVALID EXAMPLES + *************************************************************************) + + (* Variable: invalid_too_short +Invalid example that do not contain path *) + let invalid_too_short = "H\n" + + (* Variable: invalid_age +Invalid example that contain invalid age *) + let invalid_age = "H /var/tmp/js 0000 jonhsmith 60 1sss foo\n" + + (* Variable: invalid_type +Invalid example that contain invalid type (bad letter) *) + let invalid_type = "q /var/tmp/js 0000 jonhsmith 60 1s foo\n" + + (* Variable: invalid_type_num + Invalid example that contain invalid type (numeric) *) + let invalid_type_num = "1 /var/tmp/js 0000 jonhsmith 60 1s foo\n" + + (* Variable: invalid_mode +Invalid example that contain invalid mode (bad int) *) + let invalid_mode = "H /var/tmp/js 8000 jonhsmith 60 1s foo\n" + + (* Variable: invalid_mode_alpha +Invalid example that contain invalid mode (letter) *) + let invalid_mode_alpha = "H /var/tmp/js a000 jonhsmith 60 1s foo\n" + + test Tmpfiles.lns get simple = simple_tree + + test Tmpfiles.lns get complex = complex_tree + + test Tmpfiles.lns get trailing_ws = complex_tree + + test Tmpfiles.lns get empty = {}{}{} + + test Tmpfiles.lns get exclamation_mark = exclamation_mark_tree + + test Tmpfiles.lns get short = short_tree + + test Tmpfiles.lns get short_mode = short_mode_tree + + test Tmpfiles.lns get short_uid = short_uid_tree + + test Tmpfiles.lns get short_gid = short_gid_tree + + test Tmpfiles.lns get short_age = short_age_tree + + test Tmpfiles.lns get complex_arg = complex_arg_tree + + test Tmpfiles.lns get valid_second = valid_second_tree + + test Tmpfiles.lns get valid_days = valid_days_tree + + test Tmpfiles.lns get valid_age = valid_age_tree + + test Tmpfiles.lns get percent = percent_tree + + test Tmpfiles.lns get hyphen = hyphen_tree + + test Tmpfiles.lns get valid_base = valid_base_tree + + +(* failure cases *) + + test Tmpfiles.lns get invalid_too_short = * + + test Tmpfiles.lns get invalid_age = * + + test Tmpfiles.lns get invalid_type = * + + test Tmpfiles.lns get invalid_type_num = * + + test Tmpfiles.lns get invalid_mode = * + + test Tmpfiles.lns get invalid_mode_alpha = * + +(* Local Variables: *) +(* mode: caml *) +(* End: *) diff --git a/lenses/tests/test_trapperkeeper.aug b/lenses/tests/test_trapperkeeper.aug new file mode 100644 index 0000000..369e8f9 --- /dev/null +++ b/lenses/tests/test_trapperkeeper.aug @@ -0,0 +1,141 @@ +module Test_Trapperkeeper = + +(* Variable: config *) +let config = " + # This is a comment + webserver: { + bar: { + # A comment + host: localhost + port= 9000 + default-server: true + } + + foo: { + host: localhost + port = 10000 + } +} + +jruby-puppet: { + # This setting determines where JRuby will look for gems. It is also + # used by the `puppetserver gem` command line tool. + gem-home: /var/lib/puppet/jruby-gems + + # (optional) path to puppet conf dir; if not specified, will use the puppet default + master-conf-dir: /etc/puppet + + # (optional) path to puppet var dir; if not specified, will use the puppet default + master-var-dir: /var/lib/puppet + + # (optional) maximum number of JRuby instances to allow; defaults to +2 + #max-active-instances: 1 +} + + +# CA-related settings +certificate-authority: { + + # settings for the certificate_status HTTP endpoint + certificate-status: { + + # this setting contains a list of client certnames who are whitelisted to + # have access to the certificate_status endpoint. Any requests made to + # this endpoint that do not present a valid client cert mentioned in + # this list will be denied access. + client-whitelist: [] + } +} + +os-settings: { + ruby-load-path: [/usr/lib/ruby/vendor_ruby, /home/foo/ruby ] +} +\n" + +(* Test: Trapperkeeper.lns + Test full config file *) +test Trapperkeeper.lns get config = + { } + { "#comment" = "This is a comment" } + { "@hash" = "webserver" + { "@hash" = "bar" + { "#comment" = "A comment" } + { "@simple" = "host" { "@value" = "localhost" } } + { "@simple" = "port" { "@value" = "9000" } } + { "@simple" = "default-server" { "@value" = "true" } } + } + { } + { "@hash" = "foo" + { "@simple" = "host" { "@value" = "localhost" } } + { "@simple" = "port" { "@value" = "10000" } } + } + } + { } + { "@hash" = "jruby-puppet" + { "#comment" = "This setting determines where JRuby will look for gems. It is also" } + { "#comment" = "used by the `puppetserver gem` command line tool." } + { "@simple" = "gem-home" { "@value" = "/var/lib/puppet/jruby-gems" } } + { } + { "#comment" = "(optional) path to puppet conf dir; if not specified, will use the puppet default" } + { "@simple" = "master-conf-dir" { "@value" = "/etc/puppet" } } + { } + { "#comment" = "(optional) path to puppet var dir; if not specified, will use the puppet default" } + { "@simple" = "master-var-dir" { "@value" = "/var/lib/puppet" } } + { } + { "#comment" = "(optional) maximum number of JRuby instances to allow; defaults to +2" } + { "#comment" = "max-active-instances: 1" } + } + { } + { } + { "#comment" = "CA-related settings" } + { "@hash" = "certificate-authority" + { "#comment" = "settings for the certificate_status HTTP endpoint" } + { "@hash" = "certificate-status" + { "#comment" = "this setting contains a list of client certnames who are whitelisted to" } + { "#comment" = "have access to the certificate_status endpoint. Any requests made to" } + { "#comment" = "this endpoint that do not present a valid client cert mentioned in" } + { "#comment" = "this list will be denied access." } + { "@array" = "client-whitelist" } + } + } + { } + { "@hash" = "os-settings" + { "@array" = "ruby-load-path" + { "1" = "/usr/lib/ruby/vendor_ruby" } + { "2" = "/home/foo/ruby" } + } + } + { } + + +(* Test: Trapperkeeper.lns + Should parse an empty file *) +test Trapperkeeper.lns get "\n" = {} + +(* Test: Trapperkeeper.lns + Values can be quoted *) +test Trapperkeeper.lns get "os-settings: { + ruby-load-paths: [\"/usr/lib/ruby/site_ruby/1.8\"] +}\n" = + { "@hash" = "os-settings" + { "@array" = "ruby-load-paths" + { "1" = "/usr/lib/ruby/site_ruby/1.8" } + } + } + +(* Test: Trapperkeeper.lns + Keys can be quoted *) +test Trapperkeeper.lns get "test: { + \"x\": true +}\n" = + { "@hash" = "test" + { "@simple" = "x" { "@value" = "true" } } } + +(* Test: Trapperkeeper.lns + Keys can contain / (GH #7) +*) +test Trapperkeeper.lns get "test: { + \"x/y\" : z +}\n" = + { "@hash" = "test" + { "@simple" = "x/y" { "@value" = "z" } } } diff --git a/lenses/tests/test_xml.aug b/lenses/tests/test_xml.aug index 8307f0d..f78176a 100644 --- a/lenses/tests/test_xml.aug +++ b/lenses/tests/test_xml.aug @@ -775,8 +775,8 @@ test Xml.lns get "" = * (* document element must be complete *) test Xml.lns get "" = * -(* emtpy document is rejected *) -test Xml.lns get "" = * +(* accept empty document *) +test Xml.lns get "\n" = {} (* malformed element *) test Xml.lns get "" = * @@ -823,3 +823,6 @@ test Xml.lns get "" = { "a" = "#empty" { "#attribute" { "password" = "my\!pass" } } } + +test Xml.lns put "" +after set "/a" "#empty" = "\n" diff --git a/lenses/tests/test_yaml.aug b/lenses/tests/test_yaml.aug new file mode 100644 index 0000000..6353a0a --- /dev/null +++ b/lenses/tests/test_yaml.aug @@ -0,0 +1,75 @@ +module Test_YAML = + +(* Inherit test *) +test YAML.lns get "host1: + <<: *production\n" = +{ "host1" + { "<<" = "production" } +} + +test YAML.lns get " +defaults: &defaults + repo1: master + repo2: master + +# Live +production: &production + # repo3: dc89d7a + repo4: 2d39995 + # repo5: bc4a40d + +host1: + <<: *production + +host2: + <<: *defaults + repo6: branch1 + +host3: + <<: *defaults + # repo7: branch2 + repo8: branch3 +" = +{} +{ "defaults" = "defaults" + { "repo1" = "master" } + { "repo2" = "master" } +} +{} +{ "#comment" = "Live" } +{ "production" = "production" + { "#comment" = "repo3: dc89d7a" } + { "repo4" = "2d39995" } + { "#comment" = "repo5: bc4a40d" } +} +{} +{ "host1" + { "<<" = "production" } +} +{} +{ "host2" + { "<<" = "defaults" } + { "repo6" = "branch1" } +} +{} +{ "host3" + { "<<" = "defaults" } + { "#comment" = "repo7: branch2" } + { "repo8" = "branch3" } +} + +(* Ruby YAML header *) +test YAML.lns get "--- !ruby/object:Puppet::Node::Factspress RETURN)\n" = + { "@yaml" = "!ruby/object:Puppet::Node::Factspress RETURN)" } + + +(* Continued lines *) +test YAML.lns get "abc: + def: |- + ghi +\n" = + { "abc" + { "def" + { "@mval" + { "@line" = "ghi" } } } } + diff --git a/lenses/tmpfiles.aug b/lenses/tmpfiles.aug new file mode 100644 index 0000000..7d82f7c --- /dev/null +++ b/lenses/tmpfiles.aug @@ -0,0 +1,106 @@ +(* +Module: Tmpfiles + Parses systemd tmpfiles.d files + +Author: Julien Pivotto + +About: Reference + This lens tries to keep as close as possible to `man 5 tmpfiles.d` where possible. + +About: License + This file is licenced under the LGPL v2+, like the rest of Augeas. + +About: Lens Usage + To be documented + +About: Configuration files + This lens applies to /etc/tmpfiles.d/*.conf /usr/lib/tmpfiles.d/*.conf and + /run/tmpfiles.d/*.conf. See . + +About: Examples + The file contains various examples and tests. +*) + +module Tmpfiles = + autoload xfm + +(************************************************************************ + * Group: USEFUL PRIMITIVES + *************************************************************************) + +(* Group: Comments and empty lines *) + + (* View: sep_spc +Space *) + let sep_spc = Sep.space + + (* View: sep_opt_spc +Optional space (for the beginning of the lines) *) + let sep_opt_spc = Sep.opt_space + + (* View: comment +Comments *) + let comment = Util.comment + + (* View: empty +Empty lines *) + let empty = Util.empty + +(* Group: Lense-specific primitives *) + + (* View: type +One letter. Some of them can have a "+" and all can have a "!". + +Not all letters are valid. +*) + let type = /([fFwdDvpLcbCxXrRzZtThHaAm]|[AabcLp]\+)!?/ + + (* View: mode +"-", or 4 bytes. Optionally starts with a "~". *) + let mode = /(-|~?[0-7]{4})/ + + (* View: age +"-", or one of the formats seen in the manpage: 10d, 5seconds, 1y5days. +optionally starts with a "~'. *) + let age = /(-|(~?[0-9]+(s|m|min|h|d|w|ms|us|((second|minute|hour|day|week|millisecond|microsecond)s?))?)+)/ + + (* View: argument +The last field. It can contain spaces. *) + let argument = /([^# \t\n][^#\n]+)?[^# \t\n]/ + + (* View: field +Applies to the other fields: path, gid and uid fields *) + let field = /[^# \t\n]+/ + + (* View: record +A valid record, one line in the file. +Only the two first fields are mandatory. *) + let record = [ seq "record" . sep_opt_spc . + [ label "type" . store type ] . sep_spc . + [ label "path" . store field ] . ( sep_spc . + [ label "mode" . store mode ] . ( sep_spc . + [ label "uid" . store field ] . ( sep_spc . + [ label "gid" . store field ] . ( sep_spc . + [ label "age" . store age ] . ( sep_spc . + [ label "argument" . store argument ] )? )? )? )? )? . + Util.comment_or_eol ] + +(************************************************************************ + * Group: THE TMPFILES LENSE + *************************************************************************) + + (* View: lns +The tmpfiles lens. +Each line can be a comment, a record or empty. *) + let lns = ( empty | comment | record ) * + + (* View: filter *) + let filter = incl "/etc/tmpfiles.d/*.conf" + . incl "/usr/lib/tmpfiles.d/*.conf" + . incl "/run/tmpfiles.d/*.conf" + + let xfm = transform lns filter + +(* Local Variables: *) +(* mode: caml *) +(* End: *) diff --git a/lenses/trapperkeeper.aug b/lenses/trapperkeeper.aug new file mode 100644 index 0000000..0c30493 --- /dev/null +++ b/lenses/trapperkeeper.aug @@ -0,0 +1,123 @@ +(* +Module: Trapperkeeper + Parses Trapperkeeper configuration files + +Author: Raphael Pinson + +About: License + This file is licenced under the LGPL v2+, like the rest of Augeas. + +About: Lens Usage + To be documented + +About: Configuration files + This lens applies to Trapperkeeper webservice configuration files. See . + +About: Examples + The file contains various examples and tests. +*) +module Trapperkeeper = + +autoload xfm + +(************************************************************************ + * Group: USEFUL PRIMITIVES + *************************************************************************) + +(* View: empty *) +let empty = Util.empty + +(* View: comment *) +let comment = Util.comment + +(* View: sep *) +let sep = del /[ \t]*[:=]/ ":" + +(* View: sep_with_spc *) +let sep_with_spc = sep . Sep.opt_space + +(************************************************************************ + * Group: BLOCKS (FROM 1.2, FOR 0.10 COMPATIBILITY) + *************************************************************************) + +(* Variable: block_ldelim_newlines_re *) +let block_ldelim_newlines_re = /[ \t\n]+\{([ \t\n]*\n)?/ + +(* Variable: block_rdelim_newlines_re *) +let block_rdelim_newlines_re = /[ \t]*\}/ + +(* Variable: block_ldelim_newlines_default *) +let block_ldelim_newlines_default = "\n{\n" + +(* Variable: block_rdelim_newlines_default *) +let block_rdelim_newlines_default = "}" + +(************************************************************************ + * View: block_newline + * A block enclosed in brackets, with newlines forced + * and indentation defaulting to a tab. + * + * Parameters: + * entry:lens - the entry to be stored inside the block. + * This entry should not include , + * or , + * should be indented and finish with an eol. + ************************************************************************) +let block_newlines (entry:lens) (comment:lens) = + del block_ldelim_newlines_re block_ldelim_newlines_default + . ((entry | comment) . (Util.empty | entry | comment)*)? + . del block_rdelim_newlines_re block_rdelim_newlines_default + +(************************************************************************ + * Group: ENTRY TYPES + *************************************************************************) + +let opt_dquot (lns:lens) = del /"?/ "" . lns . del /"?/ "" + +(* View: simple *) +let simple = [ Util.indent . label "@simple" . opt_dquot (store /[A-Za-z0-9_.\/-]+/) . sep_with_spc + . [ label "@value" . opt_dquot (store /[^,"\[ \t\n]+/) ] + . Util.eol ] + +(* View: array *) +let array = + let lbrack = Util.del_str "[" + in let rbrack = Util.del_str "]" + in let opt_space = del /[ \t]*/ "" + in let comma = opt_space . Util.del_str "," . opt_space + in let elem = [ seq "elem" . opt_dquot (store /[^,"\[ \t\n]+/) ] + in let elems = counter "elem" . Build.opt_list elem comma + in [ Util.indent . label "@array" . store Rx.word + . sep_with_spc . lbrack . Sep.opt_space + . (elems . Sep.opt_space)? + . rbrack . Util.eol ] + +(* View: hash *) +let hash (lns:lens) = [ Util.indent . label "@hash" . store Rx.word . sep + . block_newlines lns Util.comment + . Util.eol ] + + +(************************************************************************ + * Group: ENTRY + *************************************************************************) + +(* Just for typechecking *) +let entry_no_rec = hash (simple|array) + +(* View: entry *) +let rec entry = hash (entry|simple|array) + +(************************************************************************ + * Group: LENS AND FILTER + *************************************************************************) + +(* View: lns *) +let lns = (empty|comment)* . (entry . (empty|comment)*)* + +(* Variable: filter *) +let filter = incl "/etc/puppetserver/conf.d/*" + . incl "/etc/puppetlabs/puppetserver/conf.d/*" + . Util.stdexcl + +let xfm = transform lns filter diff --git a/lenses/util.aug b/lenses/util.aug index 1418c23..72b79ec 100644 --- a/lenses/util.aug +++ b/lenses/util.aug @@ -127,6 +127,11 @@ Variable: indent let comment_c_style = comment_generic /[ \t]*\/\/[ \t]*/ "// " +(* View: comment_c_style_or_hash + A comment line, C-style or hash *) + let comment_c_style_or_hash = + comment_generic /[ \t]*((\/\/)|#)[ \t]*/ "// " + (* View: empty_generic A generic definition of Map empty lines, including empty comments *) @@ -140,10 +145,16 @@ Variable: indent Map empty lines, including empty comments *) let empty = empty_generic empty_generic_re +(* Variable: empty_c_style_re *) + let empty_c_style_re = /[ \t]*((\/\/)|(\/\*[ \t]*\*\/))?[ \t]*/ + (* View: empty_c_style Map empty lines, including C-style empty comment *) - let empty_c_style = - empty_generic /[ \t]*((\/\/)|(\/\*[ \t]*\*\/))?[ \t]*/ + let empty_c_style = empty_generic empty_c_style_re + +(* View: empty_any + Either or *) + let empty_any = empty_generic (empty_generic_re | empty_c_style_re) (* View: empty_generic_dos A generic definition of with dos newlines diff --git a/lenses/vsftpd.aug b/lenses/vsftpd.aug index 8a2b994..eb0e96f 100644 --- a/lenses/vsftpd.aug +++ b/lenses/vsftpd.aug @@ -8,7 +8,7 @@ let eol = Util.del_str "\n" let empty = Util.empty let comment = Util.comment -let bool_option_re = /anonymous_enable|local_enable|pasv_enable|port_enable|chroot_local_user|write_enable|anon_upload_enable|anon_mkdir_write_enable|anon_other_write_enable|chown_uploads|connect_from_port_20|xferlog_enable|dirmessage_enable|anon_world_readable_only|async_abor_enable|ascii_upload_enable|ascii_download_enable|one_process_model|xferlog_std_format|pasv_promiscuous|deny_email_enable|chroot_list_enable|setproctitle_enable|text_userdb_names|ls_recurse_enable|log_ftp_protocol|guest_enable|userlist_enable|userlist_deny|use_localtime|check_shell|hide_ids|listen|port_promiscuous|passwd_chroot_enable|no_anon_password|tcp_wrappers|use_sendfile|force_dot_files|listen_ipv6|dual_log_enable|syslog_enable|background|virtual_use_local_privs|session_support|download_enable|dirlist_enable|chmod_enable|secure_email_list_enable|run_as_launching_user|no_log_lock|ssl_enable|allow_anon_ssl|force_local_logins_ssl|force_local_data_ssl|ssl_sslv2|ssl_sslv3|ssl_tlsv1|tilde_user_enable|force_anon_logins_ssl|force_anon_data_ssl|mdtm_write|lock_upload_files|pasv_addr_resolve|debug_ssl|require_cert|validate_cert|require_ssl_reuse/ +let bool_option_re = /anonymous_enable|isolate|isolate_network|local_enable|pasv_enable|port_enable|chroot_local_user|write_enable|anon_upload_enable|anon_mkdir_write_enable|anon_other_write_enable|chown_uploads|connect_from_port_20|xferlog_enable|dirmessage_enable|anon_world_readable_only|async_abor_enable|ascii_upload_enable|ascii_download_enable|one_process_model|xferlog_std_format|pasv_promiscuous|deny_email_enable|chroot_list_enable|setproctitle_enable|text_userdb_names|ls_recurse_enable|log_ftp_protocol|guest_enable|userlist_enable|userlist_deny|use_localtime|check_shell|hide_ids|listen|port_promiscuous|passwd_chroot_enable|no_anon_password|tcp_wrappers|use_sendfile|force_dot_files|listen_ipv6|dual_log_enable|syslog_enable|background|virtual_use_local_privs|session_support|download_enable|dirlist_enable|chmod_enable|secure_email_list_enable|run_as_launching_user|no_log_lock|ssl_enable|allow_anon_ssl|force_local_logins_ssl|force_local_data_ssl|ssl_sslv2|ssl_sslv3|ssl_tlsv1|tilde_user_enable|force_anon_logins_ssl|force_anon_data_ssl|mdtm_write|lock_upload_files|pasv_addr_resolve|debug_ssl|require_cert|validate_cert|require_ssl_reuse/ let uint_option_re = /accept_timeout|connect_timeout|local_umask|anon_umask|ftp_data_port|idle_session_timeout|data_connection_timeout|pasv_min_port|pasv_max_port|anon_max_rate|local_max_rate|listen_port|max_clients|file_open_mode|max_per_ip|trans_chunk_size|delay_failed_login|delay_successful_login|max_login_fails|chown_upload_mode/ diff --git a/lenses/xml.aug b/lenses/xml.aug index 23ee002..60649dc 100644 --- a/lenses/xml.aug +++ b/lenses/xml.aug @@ -154,7 +154,7 @@ let doc = (sep_osp . (prolog | comment | doctype | pi_instruction))* . ((sep_osp . content) | (sep_osp . empty_element)) . (sep_osp . (comment | pi_instruction ))* . sep_osp -let lns = doc +let lns = doc | Util.empty? let filter = (incl "/etc/xml/*.xml") . (incl "/etc/xml/catalog") diff --git a/lenses/yaml.aug b/lenses/yaml.aug new file mode 100644 index 0000000..8591a7e --- /dev/null +++ b/lenses/yaml.aug @@ -0,0 +1,74 @@ +(* +Module: Yaml + Only valid for the following subset: + +> defaults: &anchor +> repo1: master +> +> host: +> # Inheritance +> <<: *anchor +> repo2: branch + +Author: Dimitar Dimitrov +*) +module YAML = + +(* Group: helpers *) +let colon = Sep.colon +let space = Sep.space +let val = store Rx.word +let eol = Util.eol +let empty = Util.empty +let comment = Util.comment_noindent + +(* +View: indent + the imposed indent is 2 spaces +*) +let indent = del /[ \t]+/ " " + +let mval = [ label "@mval" . Util.del_str "|-" . eol + . [ label "@line" . indent . store Rx.space_in . eol ]+ ] + +(* +View: inherit +> <<: *anchor +*) +let _inherit = [ key "<<" . colon . space . Util.del_str "*" . val . eol ] +let inherit = indent . _inherit . (indent . comment)* + +(* +View: repo +> { "repo" = "branch" } +*) +let _repo = [ key Rx.word . colon . space . (val | mval) . eol ] +let repo = indent . _repo . (indent . comment)* + +(* +View: anchor +> &anchor +*) +let anchor = Util.del_str "&" . val + +(* +View: entry +> host: +> # Inheritance +> <<: *anchor +> repo2: branch +*) +let entry = [ key Rx.word . colon . (space . anchor)? . eol + . (indent . comment)* + . ((inherit . (repo+)?) | repo+) + ] + +(* View: header *) +let header = [ label "@yaml" . Util.del_str "---" + . (Sep.space . store Rx.space_in)? . eol ] + +(* +View: lns + The yaml lens +*) +let lns = ((empty|comment)* . header)? . (entry | comment | empty)* diff --git a/man/augparse.pod b/man/augparse.pod index 5221a64..079e97b 100644 --- a/man/augparse.pod +++ b/man/augparse.pod @@ -95,6 +95,17 @@ resulting string is then compared to RESULT, which can be a string, the symbol B to print the result of applying LENS to STRING, or the symbol B<*> to indicate that the test should produce an exception. +=head1 AUTHOR + +David Lutterkort + +=head1 COPYRIGHT AND LICENSE + +Copyright 2007-2015 David Lutterkort + +Augeas (and augparse) are distributed under the GNU Lesser General Public +License (LGPL) + =head1 SEE ALSO B project homepage L diff --git a/man/augtool.1 b/man/augtool.1 index 090c48e..b29e17a 100644 --- a/man/augtool.1 +++ b/man/augtool.1 @@ -1,4 +1,4 @@ -.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28) +.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29) .\" .\" Standard preamble: .\" ======================================================================== @@ -133,7 +133,7 @@ .\" ======================================================================== .\" .IX Title "AUGTOOL 1" -.TH AUGTOOL 1 "2014-06-04" "Augeas 1.2.0" "Augeas" +.TH AUGTOOL 1 "2015-10-03" "Augeas 1.4.0" "Augeas" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -217,6 +217,12 @@ they need to be set up manually and loading must be initiated with a .IX Item "--span" Load span positions for nodes in the tree, as they relate to the original file. Enables the use of the \fBspan\fR command to retrieve position data. +.IP "\fB\-\-timing\fR" 4 +.IX Item "--timing" +After executing each command, print how long, in milliseconds, executing +the command took. This makes it easier to spot slow queries, usually +through \fBmatch\fR commands, and allows exploring alternative queries that +yield the same result but might be faster. .IP "\fB\-\-version\fR" 4 .IX Item "--version" Print version information and exit. The version is also in the tree under @@ -415,12 +421,10 @@ Lenses and schema definitions in \fI/usr/share/augeas/lenses\fR and \&\fI/usr/share/augeas/lenses/dist\fR .SH "AUTHOR" .IX Header "AUTHOR" -.Vb 1 -\& David Lutterkort -.Ve +David Lutterkort .SH "COPYRIGHT AND LICENSE" .IX Header "COPYRIGHT AND LICENSE" -Copyright 2007\-2012 Red Hat Inc. +Copyright 2007\-2015 David Lutterkort .PP Augeas (and augtool) are distributed under the \s-1GNU\s0 Lesser General Public License (\s-1LGPL\s0) diff --git a/man/augtool.pod b/man/augtool.pod index 78d5a20..6169a41 100644 --- a/man/augtool.pod +++ b/man/augtool.pod @@ -97,6 +97,13 @@ C command. Using this option gives the fastest startup. Load span positions for nodes in the tree, as they relate to the original file. Enables the use of the B command to retrieve position data. +=item B<--timing> + +After executing each command, print how long, in milliseconds, executing +the command took. This makes it easier to spot slow queries, usually +through B commands, and allows exploring alternative queries that +yield the same result but might be faster. + =item B<--version> Print version information and exit. The version is also in the tree under @@ -355,11 +362,11 @@ F =head1 AUTHOR - David Lutterkort +David Lutterkort =head1 COPYRIGHT AND LICENSE -Copyright 2007-2012 Red Hat Inc. +Copyright 2007-2015 David Lutterkort Augeas (and augtool) are distributed under the GNU Lesser General Public License (LGPL) diff --git a/src/ast.c b/src/ast.c index da88700..9afaa9f 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1,7 +1,7 @@ /* * ast.c: Support routines for put/get * - * Copyright (C) 2008-2011 David Lutterkort + * Copyright (C) 2008-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/augeas.c b/src/augeas.c index a1931f6..72221ae 100644 --- a/src/augeas.c +++ b/src/augeas.c @@ -1,7 +1,7 @@ /* * augeas.c: the core data structure for storing key/value pairs * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -814,14 +814,14 @@ int aug_get(const struct augeas *aug, const char *path, const char **value) { struct tree *match; int r; + if (value != NULL) + *value = NULL; + api_entry(aug); p = pathx_aug_parse(aug, aug->origin, tree_root_ctx(aug), path, true); ERR_BAIL(aug); - if (value != NULL) - *value = NULL; - r = pathx_find_one(p, &match); ERR_BAIL(aug); ERR_THROW(r > 1, aug, AUG_EMMATCH, "There are %d nodes matching %s", @@ -1142,6 +1142,7 @@ int tree_rm(struct pathx *p) { struct tree *tree, **del; int cnt = 0, ndel = 0, i; + /* set ndel to the number of trees we could possibly delete */ for (tree = pathx_first(p); tree != NULL; tree = pathx_next(p)) { if (! TREE_HIDDEN(tree)) ndel += 1; @@ -1159,12 +1160,32 @@ int tree_rm(struct pathx *p) { if (TREE_HIDDEN(tree)) continue; pathx_symtab_remove_descendants(pathx_get_symtab(p), tree); - del[i] = tree; - i += 1; + /* Collect the tree nodes that actually need to be deleted in + del. Mark the root of every subtree we are going to free by + setting tree->added. Only add a node to del if none of its + ancestors would have been freed by the time we get to freeing + that node; this avoids double frees for situations where the + path expression matches both /node and /node/child as unlinking + /node implicitly unlinks /node/child */ + int live = 1; + for (struct tree *t = tree; live && ! ROOT_P(t); t = t->parent) { + if (t->added) + live = 0; + } + if (live) { + del[i] = tree; + i += 1; + tree->added = 1; + } } + /* ndel now means: the number of trees we are actually going to delete */ + ndel = i; - for (i = 0; i < ndel; i++) - cnt += tree_unlink_raw(del[i]); + for (i = 0; i < ndel; i++) { + if (del[i] != NULL) { + cnt += tree_unlink_raw(del[i]); + } + } free(del); return cnt; @@ -1986,7 +2007,7 @@ int aug_transform(struct augeas *aug, const char *lens, } int aug_escape_name(augeas *aug, const char *in, char **out) { - int result; + int result = -1; api_entry(aug); ARG_CHECK(in == NULL, aug, "aug_escape_name: IN must not be NULL"); diff --git a/src/augeas.h b/src/augeas.h index 3225651..f4011f4 100644 --- a/src/augeas.h +++ b/src/augeas.h @@ -1,7 +1,7 @@ /* * augeas.h: public headers for augeas * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/augparse.c b/src/augparse.c index 863b1e4..20bfcce 100644 --- a/src/augparse.c +++ b/src/augparse.c @@ -1,7 +1,7 @@ /* * augparse.c: utility for parsing config files and seeing what's happening * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -55,7 +55,7 @@ static void print_version_info(struct augeas *aug) { goto error; fprintf(stderr, "augparse %s \n", version); - fprintf(stderr, "Copyright (C) 2007-2011 David Lutterkort\n"); + fprintf(stderr, "Copyright (C) 2007-2015 David Lutterkort\n"); fprintf(stderr, "License LGPLv2+: GNU LGPL version 2.1 or later\n"); fprintf(stderr, " \n"); fprintf(stderr, "This is free software: you are free to change and redistribute it.\n"); diff --git a/src/augrun.c b/src/augrun.c index b5a84d1..1dd2c91 100644 --- a/src/augrun.c +++ b/src/augrun.c @@ -1,7 +1,7 @@ /* * augrun.c: command interpreter for augeas * - * Copyright (C) 2011 David Lutterkort + * Copyright (C) 2011-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1225,6 +1225,7 @@ static void cmd_errors(struct command *cmd) { const char *last = err_get(aug, match, "lens/last_matched"); const char *next = err_get(aug, match, "lens/next_not_matched"); const char *msg = err_get(aug, match, "message"); + const char *path = err_get(aug, match, "path"); const char *kind = NULL; aug_get(aug, match, &kind); @@ -1239,6 +1240,8 @@ static void cmd_errors(struct command *cmd) { if (line != NULL) { fprintf(cmd->out, "Error in %s:%s.%s (%s)\n", filename, line, char_pos, kind); + } else if (path != NULL) { + fprintf(cmd->out, "Error in %s at node %s (%s)\n", filename, path, kind); } else { fprintf(cmd->out, "Error in %s (%s)\n", filename, kind); } diff --git a/src/augtool.c b/src/augtool.c index 7ab2c39..0603c6d 100644 --- a/src/augtool.c +++ b/src/augtool.c @@ -1,7 +1,7 @@ /* * augtool.c: * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,7 @@ #include #include #include +#include /* Global variables */ @@ -52,6 +53,7 @@ int echo_commands = 0; /* Gets also changed in main_loop */ bool print_version = false; bool auto_save = false; bool interactive = false; +bool timing = false; /* History file is ~/.augeas/history */ char *history_file = NULL; @@ -201,6 +203,21 @@ static char **rl_completion_matches(ATTRIBUTE_UNUSED const char *text, } #endif +#ifndef HAVE_RL_CRLF +static int rl_crlf(void) { + if (rl_outstream != NULL) + putc('\n', rl_outstream); + return 0; +} +#endif + +#ifndef HAVE_RL_REPLACE_LINE +static void rl_replace_line(ATTRIBUTE_UNUSED const char *text, + ATTRIBUTE_UNUSED int clear_undo) { + return; +} +#endif + static char **readline_completion(const char *text, int start, ATTRIBUTE_UNUSED int end) { if (start == 0) @@ -268,7 +285,7 @@ static void readline_init(void) { } __attribute__((noreturn)) -static void usage(void) { +static void help(void) { fprintf(stderr, "Usage: %s [OPTIONS] [COMMAND]\n", progname); fprintf(stderr, "Load the Augeas tree and modify it. If no COMMAND is given, run interactively\n"); fprintf(stderr, "Run '%s help' to get a list of possible commands.\n", @@ -293,6 +310,7 @@ static void usage(void) { fprintf(stderr, " -L, --noload do not load any files into the tree on startup\n"); fprintf(stderr, " -A, --noautoload do not autoload modules from the search path\n"); fprintf(stderr, " --span load span positions for nodes related to a file\n"); + fprintf(stderr, " --timing after executing each command, show how long it took\n"); fprintf(stderr, " --version print version information and exit.\n"); exit(EXIT_FAILURE); @@ -303,7 +321,8 @@ static void parse_opts(int argc, char **argv) { size_t loadpathlen = 0; enum { VAL_VERSION = CHAR_MAX + 1, - VAL_SPAN = VAL_VERSION + 1 + VAL_SPAN = VAL_VERSION + 1, + VAL_TIMING = VAL_SPAN + 1 }; struct option options[] = { { "help", 0, 0, 'h' }, @@ -321,6 +340,7 @@ static void parse_opts(int argc, char **argv) { { "noload", 0, 0, 'L' }, { "noautoload", 0, 0, 'A' }, { "span", 0, 0, VAL_SPAN }, + { "timing", 0, 0, VAL_TIMING }, { "version", 0, 0, VAL_VERSION }, { 0, 0, 0, 0} }; @@ -338,7 +358,7 @@ static void parse_opts(int argc, char **argv) { flags |= AUG_SAVE_NEWFILE; break; case 'h': - usage(); + help(); break; case 'r': root = optarg; @@ -377,8 +397,13 @@ static void parse_opts(int argc, char **argv) { case VAL_SPAN: flags |= AUG_ENABLE_SPAN; break; + case VAL_TIMING: + timing = true; + break; default: - usage(); + fprintf(stderr, "Try '%s --help' for more information.\n", + progname); + exit(EXIT_FAILURE); break; } } @@ -394,7 +419,7 @@ static void print_version_info(void) { goto error; fprintf(stderr, "augtool %s \n", version); - fprintf(stderr, "Copyright (C) 2007-2011 David Lutterkort\n"); + fprintf(stderr, "Copyright (C) 2007-2015 David Lutterkort\n"); fprintf(stderr, "License LGPLv2+: GNU LGPL version 2.1 or later\n"); fprintf(stderr, " \n"); fprintf(stderr, "This is free software: you are free to change and redistribute it.\n"); @@ -405,10 +430,24 @@ static void print_version_info(void) { fprintf(stderr, "Something went terribly wrong internally - please file a bug\n"); } -static int run_command(const char *line) { +static void print_time_taken(const struct timeval *start, + const struct timeval *stop) { + time_t elapsed = (stop->tv_sec - start->tv_sec)*1000 + + (stop->tv_usec - start->tv_usec)/1000; + printf("Time: %ld ms\n", elapsed); +} + +static int run_command(const char *line, bool with_timing) { int result; + struct timeval stop, start; + gettimeofday(&start, NULL); result = aug_srun(aug, stdout, line); + gettimeofday(&stop, NULL); + if (with_timing && result >= 0) { + print_time_taken(&start, &stop); + } + if (isatty(fileno(stdin))) add_history(line); return result; @@ -539,7 +578,7 @@ static int main_loop(void) { continue; } - code = run_command(line); + code = run_command(line, timing); if (code == -2) { free(line); return ret; @@ -570,12 +609,12 @@ static int run_args(int argc, char **argv) { } if (echo_commands) printf("%s%s\n", AUGTOOL_PROMPT, line); - code = run_command(line); + code = run_command(line, timing); free(line); if (code >= 0 && auto_save) if (echo_commands) printf("%ssave\n", AUGTOOL_PROMPT); - code = run_command("save"); + code = run_command("save", false); if (code < 0) { code = -1; @@ -612,12 +651,26 @@ static void add_transforms(char *ts, size_t tslen) { int main(int argc, char **argv) { int r; + struct timeval start, stop; setlocale(LC_ALL, ""); parse_opts(argc, argv); + if (timing) { + printf("Initializing augeas ... "); + fflush(stdout); + } + gettimeofday(&start, NULL); + aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE); + + gettimeofday(&stop, NULL); + if (timing) { + printf("done\n"); + print_time_taken(&start, &stop); + } + if (aug == NULL || aug_error(aug) != AUG_NOERROR) { fprintf(stderr, "Failed to initialize Augeas\n"); if (aug != NULL) diff --git a/src/builtin.c b/src/builtin.c index e674181..44d61cc 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1,7 +1,7 @@ /* * builtin.c: builtin primitives * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/errcode.c b/src/errcode.c index bb25e20..5cfac79 100644 --- a/src/errcode.c +++ b/src/errcode.c @@ -1,7 +1,7 @@ /* * errcode.c: internal interface for error reporting * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/errcode.h b/src/errcode.h index dda412f..f04d4e3 100644 --- a/src/errcode.h +++ b/src/errcode.h @@ -1,7 +1,7 @@ /* * errcode.h: internal interface for error reporting * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/fa.c b/src/fa.c index 99fd6d1..83ed388 100644 --- a/src/fa.c +++ b/src/fa.c @@ -1,7 +1,7 @@ /* * fa.c: finite automata * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1455,6 +1455,8 @@ static int minimize_hopcroft(struct fa *fa) { int *nsnum = NULL; int *nsind = NULL; int result = -1; + unsigned int nstates = 0; + int nsigma = 0; F(determinize(fa, NULL)); @@ -1474,9 +1476,8 @@ static int minimize_hopcroft(struct fa *fa) { list_for_each(s, fa->initial) { F(state_set_push(states, s)); } - unsigned int nstates = states->used; + nstates = states->used; - int nsigma; sigma = start_points(fa, &nsigma); E(sigma == NULL); @@ -2889,6 +2890,7 @@ int fa_ambig_example(struct fa *fa1, struct fa *fa2, static const char X = '\001'; static const char Y = '\002'; char *result = NULL, *s = NULL; + size_t result_len = 0; int ret = -1, r; struct fa *mp = NULL, *ms = NULL, *sp = NULL, *ss = NULL, *amb = NULL; struct fa *a1f = NULL, *a1t = NULL, *a2f = NULL, *a2t = NULL; @@ -2972,23 +2974,30 @@ int fa_ambig_example(struct fa *fa1, struct fa *fa2, if (s != NULL) { char *t; - F(ALLOC_N(result, (strlen(s)-1)/2 + 1)); + result_len = (s_len-1)/2 - 1; + F(ALLOC_N(result, result_len + 1)); t = result; int i = 0; - for (i=0; s[2*i] == X; i++) + for (i=0; s[2*i] == X; i++) { + assert((t - result) < result_len); *t++ = s[2*i + 1]; + } if (pv != NULL) *pv = t; i += 1; - for ( ;s[2*i] == X; i++) + for ( ;s[2*i] == X; i++) { + assert((t - result) < result_len); *t++ = s[2*i + 1]; + } if (v != NULL) *v = t; i += 1; - for (; 2*i+1 < strlen(s); i++) + for (; 2*i+1 < s_len; i++) { + assert((t - result) < result_len); *t++ = s[2*i + 1]; + } } ret = 0; @@ -3009,7 +3018,7 @@ int fa_ambig_example(struct fa *fa1, struct fa *fa2, FREE(s); *upv = result; if (result != NULL) - *upv_len = strlen(result); + *upv_len = result_len; return ret; error: FREE(result); diff --git a/src/fa.h b/src/fa.h index 6f72eff..57c4985 100644 --- a/src/fa.h +++ b/src/fa.h @@ -1,7 +1,7 @@ /* * fa.h: finite automata * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -79,13 +79,13 @@ int fa_compile(const char *re, size_t size, struct fa **fa); struct fa *fa_make_basic(unsigned int basic); /* Return 1 if FA accepts the basic language BASIC, which must be one of - * the constantsfrom enum FA_BASIC. + * the constants from enum FA_BASIC. */ int fa_is_basic(struct fa *fa, unsigned int basic); -/* Minimize FA using Brzozowski's algorithm. As a side-effect, the - * automaton will also be deterministic after being minimized. Modifies the - * automaton in place. +/* Minimize FA using the currently-set fa_minimization_algorithm. + * As a side effect, the automaton will also be deterministic after being + * minimized. Modifies the automaton in place. */ int fa_minimize(struct fa *fa); @@ -131,12 +131,14 @@ struct fa *fa_minus(struct fa *fa1, struct fa *fa2); */ struct fa *fa_iter(struct fa *fa, int min, int max); -/* Return 1 if the language of FA1 is contained in the language of FA2, 0 - * otherwise. +/* If successful, returns 1 if the language of FA1 is contained in the language + * of FA2, 0 otherwise. Returns a negative number if an error occurred. */ int fa_contains(struct fa *fa1, struct fa *fa2); -/* Return 1 if the language of FA1 equals the language of FA2 */ +/* If successful, returns 1 if the language of FA1 equals the language of FA2, + * 0 otherwise. Returns a negative number if an error occurred. + */ int fa_equals(struct fa *fa1, struct fa *fa2); /* Free all memory used by FA */ @@ -161,7 +163,10 @@ struct fa *fa_overlap(struct fa *fa1, struct fa *fa2); * EXAMPLE_LEN will hold the length of the example. * * Return 0 on success, and a negative numer on error. On error, *EXAMPLE - * will be NULL + * will be NULL. + * + * If *EXAMPLE is set, it is the caller's responsibility to free the string + * by calling free(). */ int fa_example(struct fa *fa, char **example, size_t *example_len); @@ -197,7 +202,7 @@ int fa_ambig_example(struct fa *fa1, struct fa *fa2, * On success, REGEXP_LEN is set to the length of REGEXP * * Return 0 on success, and a negative number on failure. The only reason - * to fail for FA_AS_REGEXP is running out of memory. + * for FA_AS_REGEXP to fail is running out of memory. */ int fa_as_regexp(struct fa *fa, char **regexp, size_t *regexp_len); diff --git a/src/get.c b/src/get.c index 96a9e7c..6479150 100644 --- a/src/get.c +++ b/src/get.c @@ -1,7 +1,7 @@ /* * parser.c: parse a configuration file according to a grammar * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/info.c b/src/info.c index fc5a17d..7e7313c 100644 --- a/src/info.c +++ b/src/info.c @@ -1,7 +1,7 @@ /* * info.c: filename/linenumber information for parser/interpreter * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/info.h b/src/info.h index 1f38cdc..2cb92de 100644 --- a/src/info.h +++ b/src/info.h @@ -1,7 +1,7 @@ /* * info.h: filename/linenumber information for parser/interpreter * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/internal.c b/src/internal.c index e149fe6..db74c17 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1,7 +1,7 @@ /* * internal.c: internal data structures and helpers * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/internal.h b/src/internal.h index 85d8ee3..8cd530e 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1,7 +1,7 @@ /* * internal.h: Useful definitions * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,6 +36,7 @@ #include #include #include +#include /* * Various parameters about env vars, special tree nodes etc. @@ -377,6 +378,8 @@ struct tree { struct tree *children; /* List of children through NEXT */ char *value; int dirty; + uint8_t added; /* only used by ns_add and tree_rm to dedupe + nodesets */ struct span *span; }; diff --git a/src/jmt.c b/src/jmt.c index 6fcde04..9b0b38e 100644 --- a/src/jmt.c +++ b/src/jmt.c @@ -1,7 +1,7 @@ /* * jmt.c: Earley parser for lenses based on Jim/Mandelbaum transducers * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/jmt.h b/src/jmt.h index ab3c062..1cee413 100644 --- a/src/jmt.h +++ b/src/jmt.h @@ -1,7 +1,7 @@ /* * jmt.h: Earley parser for lenses based on Jim/Mandelbaum transducers * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/lens.c b/src/lens.c index 37db306..83d7d57 100644 --- a/src/lens.c +++ b/src/lens.c @@ -1,7 +1,7 @@ /* * lens.c: * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1061,6 +1061,10 @@ void lens_release(struct lens *lens) { * Encoding of tree levels */ char *enc_format(const char *e, size_t len) { + return enc_format_indent(e, len, 0); +} + +char *enc_format_indent(const char *e, size_t len, int indent) { size_t size = 0; char *result = NULL, *r; const char *k = e; @@ -1073,6 +1077,8 @@ char *enc_format(const char *e, size_t len) { assert(slash != NULL); v = eq + 1; + if (indent > 0) + size += indent + 1; size += 6; /* Surrounding braces */ if (k != eq) size += 1 + (eq - k) + 1; @@ -1092,6 +1098,8 @@ char *enc_format(const char *e, size_t len) { assert(eq != NULL && slash != NULL); v = eq + 1; + for (int i=0; i < indent; i++) + *r++ = ' '; r = stpcpy(r, " { "); if (k != eq) { r = stpcpy(r, "\""); @@ -1104,6 +1112,8 @@ char *enc_format(const char *e, size_t len) { r = stpcpy(r, "\""); } r = stpcpy(r, " }"); + if (indent > 0) + *r++ = '\n'; k = slash + 1; } return result; @@ -1246,13 +1256,20 @@ static int format_union_atype(struct lens *l, char **buf, uint indent) { if (ALLOC_N(c, l->nchildren) < 0) goto done; + /* Estimate the length of the string we will build. The calculation + overestimates that length so that the logic is a little simpler than + in the loop where we actually build the string */ for (int i=0; i < l->nchildren; i++) { r = format_atype(l->children[i], c+i, indent + 2); if (r < 0) goto done; - len += strlen(c[i]) + 3; + /* We will add c[i] and some fixed characters */ + len += strlen(c[i]) + strlen("\n| ()"); + if (strlen(c[i]) < indent+2) { + /* We will add indent+2 whitespace */ + len += indent+2; + } } - len += l->nchildren - 1; if (ALLOC_N(s, len+1) < 0) goto done; @@ -1263,9 +1280,12 @@ static int format_union_atype(struct lens *l, char **buf, uint indent) { if (i > 0) { *p++ = '\n'; if (strlen(t) >= indent+2) { + /* c[i] is not just whitespace */ p = stpncpy(p, t, indent+2); t += indent+2; } else { + /* c[i] is just whitespace, make sure we indent the + '|' appropriately */ memset(p, ' ', indent+2); p += indent+2; } diff --git a/src/lens.h b/src/lens.h index dfa7cbd..957b4dc 100644 --- a/src/lens.h +++ b/src/lens.h @@ -1,7 +1,7 @@ /* * lens.h: Repreentation of lenses * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -249,6 +249,12 @@ void free_lens(struct lens *lens); * { key1 = value1 } { key2 = value2 } .. { keyN = valueN } */ char *enc_format(const char *e, size_t len); +/* Format an encoded level similar to ENC_FORMAT, but put each tree node + * on a new line indented by INDENT spaces. If INDENT is negative, produce the + * same output as ENC_FORMAT + * { key1 = value1 } { key2 = value2 } .. { keyN = valueN } + */ +char *enc_format_indent(const char *e, size_t len, int indent); #if ENABLE_DEBUG void dump_lens_tree(struct lens *lens); diff --git a/src/list.h b/src/list.h index 009dcd9..b733c8e 100644 --- a/src/list.h +++ b/src/list.h @@ -1,7 +1,7 @@ /* * list.h: Simple generic list manipulation * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/memory.c b/src/memory.c index 400f3e7..ec77c09 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,7 +1,7 @@ /* * memory.c: safer memory allocation * - * Copyright (C) 2008-2011 Daniel P. Berrange + * Copyright (C) 2008-2015 Daniel P. Berrange * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/memory.h b/src/memory.h index 305907a..6dd619c 100644 --- a/src/memory.h +++ b/src/memory.h @@ -1,7 +1,7 @@ /* * memory.c: safer memory allocation * - * Copyright (C) 2008-2011 Daniel P. Berrange + * Copyright (C) 2008-2015 Daniel P. Berrange * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/pathx.c b/src/pathx.c index 8d8dbbb..5e69767 100644 --- a/src/pathx.c +++ b/src/pathx.c @@ -1,7 +1,7 @@ /* * pathx.c: handling path expressions * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -495,11 +495,14 @@ static struct nodeset *make_nodeset(struct state *state) { return result; } +/* Add NODE to NS if it is not in NS yet. This relies on the flag + * NODE->ADDED and care must be taken that NS_CLEAR_ADDED is called on NS + * as soon as we are done adding nodes to it. + */ static void ns_add(struct nodeset *ns, struct tree *node, struct state *state) { - for (int i=0; i < ns->used; i++) - if (ns->nodes[i] == node) - return; + if (node->added) + return; if (ns->used >= ns->size) { size_t size = 2 * ns->size; if (size < 10) size = 10; @@ -508,9 +511,15 @@ static void ns_add(struct nodeset *ns, struct tree *node, ns->size = size; } ns->nodes[ns->used] = node; + node->added = 1; ns->used += 1; } +static void ns_clear_added(struct nodeset *ns) { + for (int i=0; i < ns->used; i++) + ns->nodes[i]->added = 0; +} + static struct nodeset * clone_nodeset(struct nodeset *ns, struct state *state) { @@ -1003,10 +1012,13 @@ static void eval_union(struct state *state) { RET_ON_ERROR; for (int i=0; i < r->nodeset->used; i++) { ns_add(res, r->nodeset->nodes[i], state); - RET_ON_ERROR; + if (HAS_ERROR(state)) + goto error; } state->value_pool[vind].nodeset = res; push_value(vind, state); + error: + ns_clear_added(res); } static void eval_concat_string(struct state *state) { @@ -1146,10 +1158,14 @@ static bool eval_pred(struct expr *expr, struct state *state) { } } -static void ns_remove(struct nodeset *ns, int ind) { - memmove(ns->nodes + ind, ns->nodes + ind+1, - sizeof(ns->nodes[0]) * (ns->used - (ind+1))); - ns->used -= 1; +/* Remove COUNT successive entries from NS. The first entry to remove is at + IND */ +static void ns_remove(struct nodeset *ns, int ind, int count) { + if (count < 1) + return; + memmove(ns->nodes + ind, ns->nodes + ind+count, + sizeof(ns->nodes[0]) * (ns->used - (ind+count))); + ns->used -= count; } /* @@ -1165,18 +1181,34 @@ static void ns_filter(struct nodeset *ns, struct pred *predicates, uint old_ctx_pos = state->ctx_pos; for (int p=0; p < predicates->nexpr; p++) { + int first_bad = -1; /* The index of the first non-matching node */ state->ctx_len = ns->used; state->ctx_pos = 1; for (int i=0; i < ns->used; state->ctx_pos++) { state->ctx = ns->nodes[i]; bool match = eval_pred(predicates->exprs[p], state); RET_ON_ERROR; + /* We remove non-matching nodes from NS in batches; this logic + * makes sure that we only call ns_remove at the end of a run + * of non-matching nodes + */ if (match) { - i+=1; + if (first_bad >= 0) { + ns_remove(ns, first_bad, i - first_bad); + i = first_bad + 1; + } else { + i += 1; + } + first_bad = -1; } else { - ns_remove(ns, i); + if (first_bad == -1) + first_bad = i; + i += 1; } } + if (first_bad >= 0) { + ns_remove(ns, first_bad, ns->used - first_bad); + } } state->ctx = old_ctx; @@ -1184,6 +1216,40 @@ static void ns_filter(struct nodeset *ns, struct pred *predicates, state->ctx_len = old_ctx_len; } +/* Return true if PRED is solely the predicate '[n]' as in 'foo[17]' */ +static bool position_pred(struct pred *pred) { + return pred != NULL && + pred->nexpr == 1 && + pred->exprs[0]->tag == E_VALUE && + pred->exprs[0]->type == T_NUMBER; +} + +/* Return the tree node at the position implied by STEP->PREDICATES. It is + assumed and required that STEP->PREDICATES is actually a + POSITION_PRED. + + This method hand-optimizes the important case of a path expression like + 'service[42]' +*/ +static struct tree *position_filter(struct nodeset *ns, + struct step *step, + struct state *state) { + int value_ind = step->predicates->exprs[0]->value_ind; + int number = state->value_pool[value_ind].number; + + int pos = 1; + for (int i=0; i < ns->used; i++) { + for (struct tree *node = step_first(step, ns->nodes[i]); + node != NULL; + node = step_next(step, ns->nodes[i], node), pos++) { + if (pos == number) + return node; + } + } + + return NULL; +} + /* Return an array of nodesets, one for each step in the locpath. * * On return, (*NS)[0] will contain state->ctx, and (*NS)[*MAXNS] will @@ -1217,9 +1283,11 @@ static void ns_from_locpath(struct locpath *lp, uint *maxns, struct tree *root_tree; root_tree = step_root(first_step, state->ctx, state->root_ctx); ns_add((*ns)[0], root_tree, state); + ns_clear_added((*ns)[0]); } else { for (int i=0; i < root->used; i++) ns_add((*ns)[0], root->nodes[i], state); + ns_clear_added((*ns)[0]); } if (HAS_ERROR(state)) @@ -1229,15 +1297,25 @@ static void ns_from_locpath(struct locpath *lp, uint *maxns, list_for_each(step, lp->steps) { struct nodeset *work = (*ns)[cur_ns]; struct nodeset *next = (*ns)[cur_ns + 1]; - for (int i=0; i < work->used; i++) { - for (struct tree *node = step_first(step, work->nodes[i]); - node != NULL; - node = step_next(step, work->nodes[i], node)) + if (position_pred(step->predicates)) { + struct tree *node = position_filter(work, step, state); + if (node) { ns_add(next, node, state); + ns_clear_added(next); + } + } else { + for (int i=0; i < work->used; i++) { + for (struct tree *node = step_first(step, work->nodes[i]); + node != NULL; + node = step_next(step, work->nodes[i], node)) { + ns_add(next, node, state); + } + } + ns_clear_added(next); + ns_filter(next, step->predicates, state); + if (HAS_ERROR(state)) + goto error; } - ns_filter(next, step->predicates, state); - if (HAS_ERROR(state)) - goto error; cur_ns += 1; } @@ -2468,7 +2546,11 @@ int pathx_parse(const struct tree *tree, *************************************************************************/ static bool step_matches(struct step *step, struct tree *tree) { - return (step->name == NULL || streqx(step->name, tree->label)); + if (step->name == NULL) { + return step->axis == ROOT || tree->label != NULL; + } else { + return streqx(step->name, tree->label); + } } static struct tree *tree_prev(struct tree *pos) { @@ -2937,7 +3019,7 @@ void pathx_symtab_remove_descendants(struct pathx_symtab *symtab, while (t != t->parent && t != tree) t = t->parent; if (t == tree) - ns_remove(ns, i); + ns_remove(ns, i, 1); else i += 1; } diff --git a/src/put.c b/src/put.c index 6888ef9..99cf2e1 100644 --- a/src/put.c +++ b/src/put.c @@ -1,7 +1,7 @@ /* * put.c: * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -111,18 +111,18 @@ static char *encpcpy(char *e, const char *key, const char *value) { static void regexp_match_error(struct state *state, struct lens *lens, int count, struct split *split) { - // FIXME: Split the regexp and encoding back - // into something resembling a tree level char *text = NULL; char *pat = NULL; lns_format_atype(lens, &pat); - text = enc_format(split->enc + split->start, split->end - split->start); + text = enc_format_indent(split->enc + split->start, + split->end - split->start, + 4); if (count == -1) { put_error(state, lens, - "Failed to match \n %s\n with tree\n %s", - pat, text); + "Failed to match tree\n\n%s\n with pattern\n %s", + text, pat); } else if (count == -2) { put_error(state, lens, "Internal error matching\n %s\n with tree\n %s", diff --git a/src/ref.c b/src/ref.c index c896b36..15f821b 100644 --- a/src/ref.c +++ b/src/ref.c @@ -1,7 +1,7 @@ /* * ref.c: reference counting * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/ref.h b/src/ref.h index 3314741..a4723bc 100644 --- a/src/ref.h +++ b/src/ref.h @@ -1,7 +1,7 @@ /* * ref.h: reference counting macros * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/regexp.c b/src/regexp.c index 55591b5..539ca96 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -1,7 +1,7 @@ /* * regexp.c: * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/regexp.h b/src/regexp.h index 4b7dd14..d16b6aa 100644 --- a/src/regexp.h +++ b/src/regexp.h @@ -1,7 +1,7 @@ /* * regexp.h: wrappers for regexp handling * - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/syntax.c b/src/syntax.c index ac99e0e..f497dce 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -1,7 +1,7 @@ /* * syntax.c: * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/syntax.h b/src/syntax.h index d978b03..581ac75 100644 --- a/src/syntax.h +++ b/src/syntax.h @@ -1,7 +1,7 @@ /* * syntax.h: Data types to represent language syntax * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/transform.c b/src/transform.c index 5775308..1a59187 100644 --- a/src/transform.c +++ b/src/transform.c @@ -1,7 +1,7 @@ /* * transform.c: support for building and running transformers * - * Copyright (C) 2007-2011 David Lutterkort + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/transform.h b/src/transform.h index 7f100f5..5318602 100644 --- a/src/transform.h +++ b/src/transform.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 David Lutterkort + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/src/try b/src/try index 6f66f11..a4c201f 100755 --- a/src/try +++ b/src/try @@ -31,6 +31,8 @@ elif [[ "x$1" == "xstrace" ]] ; then libtool --mode=execute /usr/bin/strace ./augtool --nostdinc < $AUGCMDS elif [[ "x$1" == "xvalgrind" ]] ; then libtool --mode=execute valgrind --leak-check=full ./augtool --nostdinc < $AUGCMDS +elif [[ "x$1" == "xcallgrind" ]] ; then + libtool --mode=execute valgrind --tool=callgrind ./augtool --nostdinc < $AUGCMDS elif [[ "x$1" == "xcli" ]] ; then shift exec ./augtool --nostdinc "$@" diff --git a/tests/Makefile.am b/tests/Makefile.am index b456354..3697862 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -28,7 +28,7 @@ lens_tests = \ lens-aptcacherngsecurity.sh \ lens-aptpreferences.sh \ lens-aptconf.sh \ - lens-aptsource.sh \ + lens-aptsources.sh \ lens-authorized_keys.sh \ lens-automaster.sh \ lens-automounter.sh \ @@ -50,6 +50,7 @@ lens_tests = \ lens-cpanel.sh \ lens-cron.sh \ lens-crypttab.sh \ + lens-csv.sh \ lens-cyrus_imapd.sh \ lens-cups.sh \ lens-darkice.sh \ @@ -110,6 +111,7 @@ lens_tests = \ lens-lvm.sh \ lens-mailscanner.sh \ lens-mailscanner_rules.sh \ + lens-masterpasswd.sh \ lens-mcollective.sh \ lens-mdadm_conf.sh \ lens-memcached.sh \ @@ -166,6 +168,7 @@ lens_tests = \ lens-redis.sh \ lens-reprepro_uploaders.sh \ lens-resolv.sh \ + lens-rhsm.sh \ lens-rmt.sh \ lens-rsyncd.sh \ lens-rsyslog.sh \ @@ -190,6 +193,7 @@ lens_tests = \ lens-ssh.sh \ lens-sshd.sh \ lens-sssd.sh \ + lens-star.sh \ lens-stunnel.sh \ lens-subversion.sh \ lens-sysconfig.sh \ @@ -198,6 +202,8 @@ lens_tests = \ lens-sysctl.sh \ lens-systemd.sh \ lens-thttpd.sh \ + lens-tmpfiles.sh \ + lens-trapperkeeper.sh \ lens-tuned.sh \ lens-up2date.sh \ lens-updatedb.sh \ @@ -215,6 +221,7 @@ lens_tests = \ lens-grub.sh \ lens-schroot.sh \ lens-xendconfsxp.sh \ + lens-yaml.sh \ lens-yum.sh ME = tests/Makefile.am @@ -261,7 +268,7 @@ noinst_SCRIPTS = $(check_SCRIPTS) noinst_PROGRAMS = leak -check_PROGRAMS = fatest test-xpath test-load test-save test-api test-run +check_PROGRAMS = fatest test-xpath test-load test-perf test-save test-api test-run TESTS_ENVIRONMENT = \ PATH='$(abs_top_builddir)/src$(PATH_SEPARATOR)'"$$PATH" \ @@ -291,6 +298,9 @@ test_api_LDADD = $(top_builddir)/src/libaugeas.la $(LIBXML_LIBS) $(GNULIB) test_run_SOURCES = test-run.c cutest.c cutest.h $(top_srcdir)/src/memory.c $(top_srcdir)/src/memory.h test_run_LDADD = $(top_builddir)/src/libaugeas.la $(LIBXML_LIBS) $(GNULIB) +test_perf_SOURCES = test-perf.c cutest.c cutest.h $(top_srcdir)/src/memory.c $(top_srcdir)/src/memory.h +test_perf_LDADD = $(top_builddir)/src/libaugeas.la $(LIBXML_LIBS) $(GNULIB) + leak_SOURCES = leak.c leak_LDADD = $(top_builddir)/src/libaugeas.la $(LIBXML_LIBS) $(GNULIB) diff --git a/tests/fatest.c b/tests/fatest.c index b63bade..5cf4282 100644 --- a/tests/fatest.c +++ b/tests/fatest.c @@ -1,7 +1,7 @@ /* * fatest.c: * - * Copyright (C) 2007-2011 Red Hat Inc. + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -395,6 +395,7 @@ static void assertAmbig(CuTest *tc, const char *regexp1, const char *regexp2, CuAssertPtrNotNull(tc, v); CuAssertStrEquals(tc, exp_upv, upv); + CuAssertIntEquals(tc, strlen(exp_upv), upv_len); CuAssertStrEquals(tc, exp_pv, pv); CuAssertStrEquals(tc, exp_v, v); free(upv); @@ -423,6 +424,31 @@ static void testAmbig(CuTest *tc) { assertNotAmbig(tc, "(a|b|c|d|abcd)", "(a|b|c|d|abcd)"); } +static void testAmbigWithNuls(CuTest *tc) { + struct fa *fa1 = make_fa(tc, "X\0ba?", 5, REG_NOERROR); + struct fa *fa2 = make_fa(tc, "a?\0Y", 4, REG_NOERROR); + char *upv, *pv, *v; + size_t upv_len; + int r = fa_ambig_example(fa1, fa2, &upv, &upv_len, &pv, &v); + CuAssertIntEquals(tc, 0, r); + CuAssertIntEquals(tc, 6, (int)upv_len); + /* u = "X\0b" */ + size_t u_len = pv - upv; + CuAssertIntEquals(tc, 3, u_len); + CuAssertIntEquals(tc, (int)'X', upv[0]); + CuAssertIntEquals(tc, (int)'\0', upv[1]); + CuAssertIntEquals(tc, (int)'b', upv[2]); + /* p = "a" */ + size_t p_len = v - pv; + CuAssertIntEquals(tc, 1, p_len); + CuAssertIntEquals(tc, (int)'a', pv[0]); + /* v = "\0Y" */ + size_t v_len = upv_len - (v - upv); + CuAssertIntEquals(tc, 2, v_len); + CuAssertIntEquals(tc, (int)'\0', v[0]); + CuAssertIntEquals(tc, (int)'Y', v[1]); +} + static void assertFaAsRegexp(CuTest *tc, const char *regexp) { char *re; size_t re_len; @@ -657,6 +683,7 @@ int main(int argc, char **argv) { SUITE_ADD_TEST(suite, testOverlap); SUITE_ADD_TEST(suite, testExample); SUITE_ADD_TEST(suite, testAmbig); + SUITE_ADD_TEST(suite, testAmbigWithNuls); SUITE_ADD_TEST(suite, testAsRegexp); SUITE_ADD_TEST(suite, testAsRegexpMinus); SUITE_ADD_TEST(suite, testRangeEnd); diff --git a/tests/lens-test-1 b/tests/lens-test-1 index 2916ac0..4fe4b68 100755 --- a/tests/lens-test-1 +++ b/tests/lens-test-1 @@ -12,5 +12,6 @@ t=$LENS_DIR/tests/test_$me.aug if [ -n "$VALGRIND" ] ; then exec $VALGRIND $AUGPARSE --nostdinc -I "$LENS_DIR" "$t" else + export MALLOC_CHECK_=3 exec augparse --nostdinc -I "$LENS_DIR" "$t" fi diff --git a/tests/modules/pass_format_atype.aug b/tests/modules/pass_format_atype.aug new file mode 100644 index 0000000..49cf07f --- /dev/null +++ b/tests/modules/pass_format_atype.aug @@ -0,0 +1,12 @@ +module Pass_format_atype = + +(* format_atype in lens.c had an allocation bug that would corrupt memory + and later lead to unpleasant behavior like segfaults. This nonsensical + lens, when formatted, triggers that problem. + + For this test to make sense, it needs to be run with MALLOC_CHECK_=2 so + that glibc complains and aborts the test +*) + +let l = [label "l"]|del /x/ "x" +let _ = lens_format_atype l diff --git a/tests/root/etc/sysconfig/network-scripts/ifcfg-Auto-ALICE-WLAN38_(automatisch) b/tests/root/etc/sysconfig/network-scripts/ifcfg-Auto-ALICE-WLAN38_(automatisch) deleted file mode 100644 index e69de29..0000000 diff --git a/tests/root/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ!Box_Fon_WLAN_7112 b/tests/root/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ!Box_Fon_WLAN_7112 deleted file mode 100644 index 9dde9f5..0000000 --- a/tests/root/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ!Box_Fon_WLAN_7112 +++ /dev/null @@ -1,14 +0,0 @@ -ESSID="FRITZ!Box Fon WLAN 7112" -MODE=Managed -KEY_MGMT=WPA-PSK -WPA_PSK_FLAGS=user -TYPE=Wireless -BOOTPROTO=dhcp -DEFROUTE=yes -IPV4_FAILURE_FATAL=yes -NAME="Auto FRITZ!Box Fon WLAN 7112" -ONBOOT=yes -PEERDNS=yes -PEERROUTES=yes -IPV6INIT=no -USERS=lutter diff --git a/tests/test-api.c b/tests/test-api.c index cc2cc87..9d739ef 100644 --- a/tests/test-api.c +++ b/tests/test-api.c @@ -1,7 +1,7 @@ /* * test-api.c: test public API functions for conformance * - * Copyright (C) 2009-2011 Red Hat Inc. + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -121,6 +121,13 @@ static void testGet(CuTest *tc) { CuAssertPtrNotNull(tc, value); CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug)); + /* aug_get should set VALUE to NULL even if the path expression is invalid + Issue #372 */ + value = (const char *) 7; + r = aug_get(aug, "[invalid path]", &value); + CuAssertIntEquals(tc, -1, r); + CuAssertPtrEquals(tc, NULL, value); + aug_close(aug); } @@ -642,6 +649,20 @@ static void testAugEscape(CuTest *tc) { free(out); } +static void testRm(CuTest *tc) { + struct augeas *aug; + int r; + + aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_MODL_AUTOLOAD); + CuAssertPtrNotNull(tc, aug); + + r = aug_set(aug, "/files/1/2/3/4/5", "1"); + CuAssertRetSuccess(tc, r); + + r = aug_rm(aug, "/files//*"); + CuAssertIntEquals(tc, 5, r); +} + int main(void) { char *output = NULL; CuSuite* suite = CuSuiteNew(); @@ -661,6 +682,7 @@ int main(void) { SUITE_ADD_TEST(suite, testTextStore); SUITE_ADD_TEST(suite, testTextRetrieve); SUITE_ADD_TEST(suite, testAugEscape); + SUITE_ADD_TEST(suite, testRm); abs_top_srcdir = getenv("abs_top_srcdir"); if (abs_top_srcdir == NULL) diff --git a/tests/test-interpreter.sh b/tests/test-interpreter.sh index 1abce96..303beb8 100755 --- a/tests/test-interpreter.sh +++ b/tests/test-interpreter.sh @@ -32,7 +32,7 @@ run_tests_plain () fi printf "$action %-30s ... " $(basename $g .aug) set +e - errs=$(augparse --nostdinc -I ${MODULES} $g 2>&1 > /dev/null) + errs=$(MALLOC_CHECK_=3 augparse --nostdinc -I ${MODULES} $g 2>&1 > /dev/null) ret=$? set -e if [ $ret -eq $ret_fail ]; then diff --git a/tests/test-load.c b/tests/test-load.c index fb63a62..79151ed 100644 --- a/tests/test-load.c +++ b/tests/test-load.c @@ -1,7 +1,7 @@ /* * test-load.c: test the aug_load functionality * - * Copyright (C) 2009-2011 Red Hat Inc. + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-perf.c b/tests/test-perf.c new file mode 100644 index 0000000..0de74ba --- /dev/null +++ b/tests/test-perf.c @@ -0,0 +1,111 @@ +/* + * test-perf.c: test performance of API functions + * + * Copyright (C) 2009-2015 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: David Lutterkort + */ + +#include + +#include "augeas.h" + +#include "cutest.h" +#include "internal.h" + +#include +#include + +static const char *abs_top_srcdir; +static char *root; +static char *loadpath; + +#define die(msg) \ + do { \ + fprintf(stderr, "%d: Fatal error: %s\n", __LINE__, msg); \ + exit(EXIT_FAILURE); \ + } while(0) + +#define time_taken(start, stop) \ + ((stop.tv_sec - start.tv_sec)*1000 + (stop.tv_usec - start.tv_usec)/1000) + +/* Test performance of basic predicate [1..n] with many nodes */ +static void testPerfPredicate(CuTest *tc) { + const char *value; + char *path; + struct timeval stop, start; + struct augeas *aug; + + aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD); + CuAssertPtrNotNull(tc, aug); + + gettimeofday(&start, NULL); + + for (int i=1; i <= 5000; i++) { + if (!asprintf(&path, "/test/service[%i]", i)) + die("failed to generate set path"); + aug_set(aug, path, "test"); + } + + for (int i=1; i <= 5000; i++) { + if (!asprintf(&path, "/test/service[%i]", i)) + die("failed to generate set path"); + aug_get(aug, path, &value); + CuAssertStrEquals(tc, "test", value); + } + + gettimeofday(&stop, NULL); + printf("testPerfPredicate = %lums\n", time_taken(start, stop)); + + aug_close(aug); +} + +int main(void) { + char *output = NULL; + CuSuite* suite = CuSuiteNew(); + CuSuiteSetup(suite, NULL, NULL); + + SUITE_ADD_TEST(suite, testPerfPredicate); + + abs_top_srcdir = getenv("abs_top_srcdir"); + if (abs_top_srcdir == NULL) + die("env var abs_top_srcdir must be set"); + + if (asprintf(&root, "%s/tests/root", abs_top_srcdir) < 0) { + die("failed to set root"); + } + + if (asprintf(&loadpath, "%s/lenses", abs_top_srcdir) < 0) { + die("failed to set loadpath"); + } + + CuSuiteRun(suite); + CuSuiteSummary(suite, &output); + CuSuiteDetails(suite, &output); + printf("%s\n", output); + free(output); + return suite->failCount; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/tests/test-run.c b/tests/test-run.c index ba1b50d..2e4e6dc 100644 --- a/tests/test-run.c +++ b/tests/test-run.c @@ -1,7 +1,7 @@ /* * test-run.c: test the aug_srun API function * - * Copyright (C) 2009-2011 Red Hat Inc. + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/test-save.c b/tests/test-save.c index 4b6470f..faa24e6 100644 --- a/tests/test-save.c +++ b/tests/test-save.c @@ -1,7 +1,7 @@ /* * test-save.c: test various aspects of saving * - * Copyright (C) 2009-2011 Red Hat Inc. + * Copyright (C) 2009-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -355,6 +355,11 @@ static void testPathEscaping(CuTest *tc) { * used to lead to a SEGV */ static void testSaveNoPermission(CuTest *tc) { + if (getuid() == 0) { + puts("pending (testSaveNoPermission): can't test permissions under root account"); + return; + } + int r; char *path = NULL; const char *v; diff --git a/tests/test-xpath.c b/tests/test-xpath.c index 335e7bf..ceb96a7 100644 --- a/tests/test-xpath.c +++ b/tests/test-xpath.c @@ -1,7 +1,7 @@ /* * test-xpath.c: check that XPath expressions yield the expected result * - * Copyright (C) 2007-2011 Red Hat Inc. + * Copyright (C) 2007-2015 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/tests/xpath.tests b/tests/xpath.tests index e89cf98..ace3d5e 100644 --- a/tests/xpath.tests +++ b/tests/xpath.tests @@ -321,9 +321,7 @@ test escape1 /files/etc/sysconfig/network-scripts/* /files/etc/sysconfig/network-scripts/ifcfg-eth0 /files/etc/sysconfig/network-scripts/ifcfg-wlan0 /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\) - /files/etc/sysconfig/network-scripts/ifcfg-Auto_FRITZ\!Box_Fon_WLAN_7112 /files/etc/sysconfig/network-scripts/ifcfg-lo - /files/etc/sysconfig/network-scripts/ifcfg-Auto-ALICE-WLAN38_\(automatisch\) /files/etc/sysconfig/network-scripts/ifcfg-br0 test escape2 /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\)/DEVICE @@ -331,3 +329,7 @@ test escape2 /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ t test escape3 /files/etc/sysconfig/network-scripts/*[DEVICE = 'weird'] /files/etc/sysconfig/network-scripts/ifcfg-weird\ \[\!\]\ \(used\ to\ fail\) + +# Test matching in the presence of hidden nodes +test hidden1 /files/etc/security/limits.conf/*[label() != '#comment'][1] + /files/etc/security/limits.conf/domain[1] = @jackuser -- 2.7.4