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
Andrew Colin Kissa <andrew@topdog.za.net>
Francois Lebel <francoislebel@gmail.com>
Frédéric Lespez <frederic.lespez@free.fr>
+ Miroslav Lichvar <mlichvar@redhat.com>
Jasper Lievisse Adriaanse <jasper@humppa.nl>
Tom Limoncelli <tal@whatexit.org>
Erinn Looney-Triggs <erinn.looneytriggs@gmail.com>
Frank Grötzner <frank@unforgotten.de>
Pino Toscano <ptoscano@redhat.com>
Geoffrey Gardella <gardella@gmail.com>
+ Matt Dainty <matt@bodgit-n-scarper.com>
+ Jan Doleschal <jan.doleschal@lgl.bwl.de>
+ Joe Topjian <joe@topjian.net>
+ Julien Pivotto <roidelapluie@inuits.eu>
+ Gregory Smith <gasmith@nutanix.com>
+ Justin Akers <justin.akers@opengear.com>
+ Oliver Mangold <o.mangold@gmail.com>
+ Geoff Williams <geoff.williams@puppetlabs.com>
+ Florian Chazal <florianchazal@gmail.com>
+ Dimitar Dimitrov <mitkofr@yahoo.fr>
+ Cédric Bosdonnat <cedric.bosdonnat@free.fr>
+ Christoph Maser <cmaser@gmx.de>
+ Chris Reeves <chris.reeves@york.ac.uk>
+ Gerlof Fokkema <gerlof.fokkema@gmail.com>
+ Daniel Trebbien <dtrebbien@gmail.com>
+++ /dev/null
-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
--- /dev/null
+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
+```
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
+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
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
])
-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])
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
-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
-Format: 1.51
+Format: 1.52
Title: Augeas Documentation
-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
-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
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
-Format: 1.51
+Format: 1.52
Title: Augeas Documentation
# 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
+
# --------------------------------------------------------------------------
#
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)
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)
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)
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)
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 {
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)
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)
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)
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 {
-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
(* View: empty
A C-style empty line *)
-let empty = Util.empty_c_style
+let empty = Util.empty_any
(* View: indent
An indentation *)
(* 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 *)
* 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"
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
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.
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
************************************************************************)
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
let flags = "dumponexit"
| "generatecommandkey"
| "lock_all"
+ | "manual"
| "noclientlog"
| "rtconutc"
| "rtcsync"
(* 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"
(************************************************************************
* 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 <address> <options>
+ - server|peer|pool <address> <options>
+ - allow|deny|cmdallow|cmddeny [all] [<address[/subnet]>]
- log <options>
- broadcast <interval> <address> <optional port>
- fallbackdrift <min> <max>
- initstepslew <threshold> <addr> <optional extra addrs>
- - local stratum <int>
+ - local <options>
- mailonchange <emailaddress> <threshold>
- makestep <threshold> <limit>
- maxchange <threshold> <delay> <limit>
+ - ratelimit|cmdratelimit <options>
+ - refclock <driver> <parameter> <options>
+ - smoothtime <maxfreq> <maxwander> <options>
+ - tempcomp <sensorfile> <interval> (<t0> <k0> <k1> <k2> | <pointfile> )
*)
(* 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
*)
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
(* 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
. [ 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
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" .
--- /dev/null
+(*
+Module: CSV
+ Generic CSV lens collection
+
+Author: Raphael Pinson <raphael.pinson@camptocamp.com>
+
+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 <Test_CSV> 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 ";"
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 ]
. [ 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) =
. 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
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_.!*-]*/
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 ]
(* 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 *)
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
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)* . /'/
*****************************************************************)
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 *)
let inner = (sep_spc . argv arg_sec)? . sep_osp .
dels ">" . opt_eol . ((body|comment) . (body|empty|comment)*)? .
indent . dels "</" in
- let kword = key word in
- let dword = del word "a" in
- [ indent . dels "<" . square kword inner dword . del ">" ">" . 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 /<perl>/i "<Perl>"
+ . store /[^<]*/
+ . del /<\/perl>/i "</Perl>" . eol ]
+
let rec content = section (content|directive)
+ | perl_section
let lns = (content|directive|comment|empty)*
(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") .
(* 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
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
(* 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]*/ " "
let source = [ key "source" . sep_spc . sto_to_eol ]
(************************************************************************
+ * SOURCE-DIRECTORY
+ *************************************************************************)
+
+let source_directory = [ key "source-directory" . sep_spc . sto_to_eol ]
+
+(************************************************************************
* STANZAS
*************************************************************************)
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
(************************************************************************
(* 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
(* 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]/
An IP <space> 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 <comment>. *)
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
(* 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
(* 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 *)
| block "static_routes" static_routes_field
-(* View: global_conf
+(* View: global_conf
A global configuration entry *)
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
(* 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
)
| 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
Author: Raphaël Pinson <raphink@gmail.com>
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.
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 *)
| 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
--- /dev/null
+(*
+ Module: MasterPasswd
+ Parses /etc/master.passwd
+
+ Author: Matt Dainty <matt@bodgit-n-scarper.com>
+
+ 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
|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
|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
|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 =
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")
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
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"
(* 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 ]
. 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 )*
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")
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"
(* OpenVPN module for Augeas
Author: Raphael Pinson <raphink@gmail.com>
+ Author: Justin Akers <dafugg@gmail.com>
Reference: http://openvpn.net/index.php/documentation/howto.html
+ Reference: https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage
+
+ TODO: Inline file support
*)
(* 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]*/ "# "
(************************************************************************
* 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
* - 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"
| "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 ]
(************************************************************************
* 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")
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
(* 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
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 ]
(* 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]*/) ]
(* View: lns
the Puppetfile lens *)
-let lns = (Util.empty | Util.comment | forge | metadata | mod)*
+let lns = (Util.empty | Util.comment | forge | metadata | mod | moduledir )*
(* 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 *)
| 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 =
| 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
| 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 <Erlang.application> config *)
About: License
This file is licenced under the LGPL v2+, like the rest of Augeas.
-
About: Lens Usage
See <lns>.
| "sections"
| "binaries"
| "architectures"
+ | "distribution"
(* View: condition_field
A single condition field is an 'or' node.
. 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
<by> statements define who is allowed to upload.
It can be simple keywords, like "anybody" or "unsigned",
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]
> $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 <allow> statement
+ or a <group> definition.
+ *)
+let entry = allow | group
+
(* View: lns
The lens is made of <Util.empty>, <Util.comment> and <entry> lines *)
let lns = (Util.empty|Util.comment|entry)*
--- /dev/null
+(*
+Module: Rhsm
+ Parses subscription-manager config files
+
+Author: Dominic Cleal <dcleal@redhat.com>
+
+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 <filter>.
+*)
+
+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
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
(* 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"]*/) ]
(* 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
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 . /[\*@]\}/
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 ] *
(* 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
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
. 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
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
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
. 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
| loop_until entry
| case entry entry_noeol
| function entry
+ | subshell entry
let lns_norec = del_empty* . (comment | entry_eol) *
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-*" .
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"
. 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"
. 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
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"
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 *)
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 ]]
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
| 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
--- /dev/null
+(*
+Module: Star
+ Parses star's configuration file
+
+Author: Cedric Bosdonnat <cbosdonnat@suse.com>
+
+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
(* 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 <defaults>. Accept one optional '!'
+ * and produce a 'negate' node if there is one.
+ *************************************************************************)
+let negate_node = [ del "!" "!" . label "negate" ]
+
+(************************************************************************
+ * View: negate_or_value
+ * A <del_negate>, 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
* > 'Cmnd_Alias' Cmnd_Alias (':' Cmnd_Alias)*
*************************************************************************)
let alias = user_alias | runas_alias | host_alias | cmnd_alias
+
(************************************************************************
* Group: DEFAULTS
*************************************************************************)
+
(************************************************************************
* View: default_type
* Type definition for <defaults>
[ label "type" . value ]
(************************************************************************
- * View: del_negate
- * Delete an even number of '!' signs
- *************************************************************************)
-let del_negate = del /(!!)*/ ""
-
-(************************************************************************
- * View: negate_node
- * Negation of boolean values for <defaults>. 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 <defaults>
*
*)
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 *)
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" = { }
-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"
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: *)
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
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 =
{ "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" }
{ "address" = "192.168.100.255" }
{ "port" = "123" }
}
- { }
{ "fallbackdrift"
{ "min" = "16" }
{ "max" = "19" }
{ "delay" = "1" }
{ "limit" = "2" }
}
+ { "maxdistance" = "1.0" }
+ { "maxdrift" = "100" }
{ "initstepslew"
{ "threshold" = "30" }
{ "address" = "foo.bar.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: *)
--- /dev/null
+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" } }
+
{ "arg" = "allow,deny" }
}
}
- { }
{ "#comment" = "Restrict access to the admin pages..." }
{ "Location"
{ "arg" = "/admin" }
{ "arg" = "allow,deny" }
}
}
- { }
{ "#comment" = "Restrict access to configuration files..." }
{ "Location"
{ "arg" = "/admin/conf" }
{ "arg" = "allow,deny" }
}
}
- { }
{ "#comment" = "Set the default printer/job policies..." }
{ "Policy"
{ "arg" = "default" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "Limit"
{ "arg" = "Send-Document" }
{ "arg" = "Send-URI" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "All administration operations require an administrator to authenticate..." }
{ "Limit"
{ "arg" = "CUPS-Add-Modify-Printer" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "All printer operations require a printer operator to authenticate..." }
{ "Limit"
{ "arg" = "Pause-Printer" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "Only the owner or an administrator can cancel or authenticate a job..." }
{ "Limit"
{ "arg" = "Cancel-Job" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "Limit"
{ "arg" = "All" }
{ "directive" = "Order"
}
}
}
- { }
{ "#comment" = "Set the authenticated printer/job policies..." }
{ "Policy"
{ "arg" = "authenticated" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "Limit"
{ "arg" = "Send-Document" }
{ "arg" = "Send-URI" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "All administration operations require an administrator to authenticate..." }
{ "Limit"
{ "arg" = "CUPS-Add-Modify-Printer" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "All printer operations require a printer operator to authenticate..." }
{ "Limit"
{ "arg" = "Pause-Printer" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "#comment" = "Only the owner or an administrator can cancel or authenticate a job..." }
{ "Limit"
{ "arg" = "Cancel-Job" }
{ "arg" = "deny,allow" }
}
}
- { }
{ "Limit"
{ "arg" = "All" }
{ "directive" = "Order"
{ "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"
{ "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
{ "@nisdefault"
{ "password" = "" }
{ "gid" = "" } }
+
+test Group.lns get "+:*::\n" =
+ { "@nisdefault"
+ { "password" = "*" }
+ { "gid" = "" } }
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
{ "arg" = "all" }
}
}
- { }
{ "directive" = "ScriptAlias"
{ "arg" = "/cgi-bin/" }
{ "arg" = "/usr/lib/cgi-bin/" }
{ "arg" = "all" }
}
}
- { }
{ "directive" = "ErrorLog"
{ "arg" = "/var/log/apache2/error.log" }
}
{ "arg" = "::1/128" }
}
}
- { }
}
(* Eol comment *)
}
}
}
- {}
{ "IfVersion"
{ "arg" = ">=" }
{ "arg" = "2.4" }
{ "#comment" = "one comment" }
{ "#comment" = "another comment" }
}
+
+(* Issue #307: backslashes in regexes *)
+test Httpd.lns get "<VirtualHost *:80>
+ RewriteRule ^/(.*) http\:\/\/example\.com\/$1 [L,R,NE]
+ RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
+</VirtualHost>\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 "<IfModule>
+</ifModule>\n" =
+ { "IfModule" }
+
+(* https://github.com/letsencrypt/letsencrypt/issues/1693 *)
+test Httpd.lns get "<IfModule mod_ssl.c>
+ <VirtualHost *:443>
+ ServerAdmin admin@example.com
+ </VirtualHost> </IfModule>\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 "<VirtualHost *:80>
+ WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=_graphite group=_graphite
+</VirtualHost>\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 "<Perl>
+ Apache::AuthDBI->setCacheTime(600);
+</Perl>\n" =
+ { "Perl" = "\n Apache::AuthDBI->setCacheTime(600);\n" }
+
+(* Line continuations inside VirtualHost blocks *)
+test Httpd.lns get "<VirtualHost \\
+ 0.0.0.0:7080 \\
+ [00000:000:000:0000::2]:7080 \\
+ 0.0.0.0:7080 \\
+ 127.0.0.1:7080 \\
+ >
+</VirtualHost>\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 "<VirtualHost \\
+ 0.0.0.0:7080 \\
+ \\
+ 0.0.0.0:7080 \\
+ \\
+ >
+</VirtualHost>\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 "<FilesMatch \.php$>
+ ExpiresActive Off
+</FilesMatch>\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 "<FilesMatch \ test\.php$></FilesMatch>\n" =
+ { "FilesMatch"
+ { "arg" = "\ test\.php$" } }
\"\\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
{ "entry" = "\\eOd"
{ "mapping" = "backward-word" }
}
+ { "@else"
+ { "entry" = "\\e[G"
+ { "mapping" = "\",\"" }
+ }
+ }
}
{ }
{ "#comment" = "for non RH/Debian xterm, can't hurt for RH/Debian xterm" }
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" }
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" }
{ "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\": {
\"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" } }
+ { } }
(* 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
{ "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"
{ "#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" }
{ "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"
{ "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" }
{ "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" }
{ "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 =
(* 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 =
(* 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 =
(* 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 =
(* 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" } }
--- /dev/null
+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"
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
failback manual
rr_weight priorities
no_path_retry 5
+ flush_on_last_del yes
}
multipath {
wwid 1DEC_____321816758474
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"
{ "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"
{ "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" } } }
{ "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
*)
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
{ "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 {
}
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" } } } }
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 =
{ "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
(* Bug #297: tos directive *)
test Ntp.tos get "tos maxdist 16\n" =
{ "tos"
- { "maxdist" = "16" } }
\ No newline at end of file
+ { "maxdist" = "16" } }
(* 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" }
{ "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" }
{ "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" }
{ "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" }
{ "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" }
{ "string" = "instant_app" }
}
}
+ { }
{ "entry" = "admin_tags"
{ "array" }
}
}
}
+ { }
}
}
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 =
{ "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"
{ "group" = "nobody" }
{ "persist-key" }
{ "persist-tun" }
- { "status" = "openvpn-status.log" }
+ { "status"
+ { "file" = "openvpn-status.log" }
+ }
{ "log" = "openvpn.log" }
{ "log-append" = "openvpn.log" }
{ "verb" = "3" }
{ "server" = "10.0.5.20" }
{ "port" = "1193" }
{ "pwfile" = "/etc/openvpn/mpass" } }
- { "script-security" = "3 system" }
- { "mssfix" = "1300" }
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 =
{ "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 <connection> 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 <connection> 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" }
+ { }
module Test_properties =
let conf = "
+#
# Test tomcat properties file
#tomcat.commented.value=1
# config
let lns = Properties.lns
test lns get conf =
- { }
+ { } { }
{ "#comment" = "Test tomcat properties file" }
{ "#comment" = "tomcat.commented.value=1" }
{ "#comment" = "config" }
set "tomcat.port" "99";
set "tomcat.application.host" "foo.network.com"
= "
+#
# Test tomcat properties file
#tomcat.commented.value=1
# config
{ "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" } }
{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 *)
{ "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 "
{ "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.
{ "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" } }
--- /dev/null
+(*
+Module: Test_Rhsm
+ Provides unit tests and examples for the <Rhsm> 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" }
+ }
*.* @@2.7.4.1
*.emerg :omusrmsg:*
*.emerg :omusrmsg:foo,bar
+*.emerg | /dev/xconsole
"
(* Test: Rsyslog.lns *)
{ "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" =
{ "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" } } }
{ "@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\"" }
;;
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" } } } }
esac\n" =
{ "@case" = "$f"
- { "@case_entry" = "a"
+ { "@case_entry"
+ { "@pattern" = "a" }
{ "B" = "C" } }
}
;;
esac\n" =
{ "@case" = "$f"
- { "@case_entry" = "a"
+ { "@case_entry"
+ { "@pattern" = "a" }
{ "B" = "C" } }
- { "@case_entry" = "b"
+ { "@case_entry"
+ { "@pattern" = "b" }
{ "A" = "D" } } }
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" } } }
;;
esac\n" =
{ "@case" = "$a"
- { "@case_entry" = "*" } }
+ { "@case_entry" { "@pattern" = "*" } } }
(* case variables can be surrounded by double quotes *)
test Shellvars.lns get "case \"${options}\" in
;;
esac\n" =
{ "@case" = "\"${options}\""
- { "@case_entry" = "*debug*"
+ { "@case_entry"
+ { "@pattern" = "*debug*" }
{ "@builtin" = "shift" } } }
(* Double quoted values can have newlines *)
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"
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 *)
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" }
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: *)
manderso = MarkAnderson MarkusAndersonus
users = @account
nobody = *
+;commented = SomeOne
"
(* Test: Simplevars.lns *)
{ "username" = "@account" } }
{ "nobody"
{ "username" = "*" } }
+ { "#comment" = "commented = SomeOne" }
# I like comments
very.useful-key my=value
-
+a.flag
"
let lns = Spacevars.lns
{ "#comment" = "I like comments"}
{ "very.useful-key" = "my=value" }
{}
- {}
+ { "a.flag" }
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 =
{ "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
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" }
+ }
+
--- /dev/null
+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" } }
{ "host_group"
{ "host" = "ALPHA" }
{ "command" = "/usr/bin/su [!-]*" }
- { "command" = "!/usr/bin/su *root*" } } }
+ { "command" = "/usr/bin/su *root*"
+ { "negate" } } } }
{}
{ "spec"
{ "user" = "@my\ admin\ group" }
{ "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" } }
+ }
+ }
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]"
--- /dev/null
+(*
+Module: Test_Tmpfiles
+ Provides unit tests and examples for the <Tmpfiles> 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 <simple> *)
+ 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 <complex> and <trailing_ws> *)
+ 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 <exclamation_mark> *)
+ 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 <short> *)
+ 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 <short_mode> *)
+ 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 <short_uid> *)
+ 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 <short_gid> *)
+ 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 <short_age> *)
+ 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 <complex_arg> *)
+ 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 <valid_age> *)
+ 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 <valid_second> *)
+ 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 <valid_days> *)
+ 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 <percent> *)
+ 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 <hyphen> *)
+ 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 <valid_base> *)
+ 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: *)
--- /dev/null
+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 <num-cpus>+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 <num-cpus>+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" } } }
(* document element must be complete *)
test Xml.lns get "<doc>" = *
-(* emtpy document is rejected *)
-test Xml.lns get "" = *
+(* accept empty document *)
+test Xml.lns get "\n" = {}
(* malformed element *)
test Xml.lns get "<a><A@/></a>" = *
test Xml.lns get "<a password=\"my\!pass\" />" =
{ "a" = "#empty"
{ "#attribute" { "password" = "my\!pass" } } }
+
+test Xml.lns put ""
+after set "/a" "#empty" = "<a/>\n"
--- /dev/null
+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" } } } }
+
--- /dev/null
+(*
+Module: Tmpfiles
+ Parses systemd tmpfiles.d files
+
+Author: Julien Pivotto <roidelapluie@inuits.eu>
+
+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 <filter>.
+
+About: Examples
+ The <Test_Tmpfiles> 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: *)
--- /dev/null
+(*
+Module: Trapperkeeper
+ Parses Trapperkeeper configuration files
+
+Author: Raphael Pinson <raphael.pinson@camptocamp.com>
+
+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 <filter>.
+
+About: Examples
+ The <Test_Trapperkeeper> 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 <Util.empty>,
+ * <Util.comment> or <Util.comment_noindent>,
+ * 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
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 <empty>
Map empty lines, including empty comments *)
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 <empty> or <empty_c_style> *)
+ let empty_any = empty_generic (empty_generic_re | empty_c_style_re)
(* View: empty_generic_dos
A generic definition of <empty> with dos newlines
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/
((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")
--- /dev/null
+(*
+Module: Yaml
+ Only valid for the following subset:
+
+> defaults: &anchor
+> repo1: master
+>
+> host:
+> # Inheritance
+> <<: *anchor
+> repo2: branch
+
+Author: Dimitar Dimitrov <mitkofr@yahoo.fr>
+*)
+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)*
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 <lutter@watzmann.net>
+
+=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<Augeas> project homepage L<http://www.augeas.net/>
-.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.29)
.\"
.\" Standard preamble:
.\" ========================================================================
.\" ========================================================================
.\"
.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
.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
\&\fI/usr/share/augeas/lenses/dist\fR
.SH "AUTHOR"
.IX Header "AUTHOR"
-.Vb 1
-\& David Lutterkort <dlutter@redhat.com>
-.Ve
+David Lutterkort <lutter@watzmann.net>
.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)
Load span positions for nodes in the tree, as they relate to the original
file. Enables the use of the B<span> 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<match> 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
=head1 AUTHOR
- David Lutterkort <dlutter@redhat.com>
+David Lutterkort <lutter@watzmann.net>
=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)
/*
* 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
/*
* 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
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",
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;
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;
}
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");
/*
* 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
/*
* 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
goto error;
fprintf(stderr, "augparse %s <http://augeas.net/>\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, " <http://www.gnu.org/licenses/lgpl-2.1.html>\n");
fprintf(stderr, "This is free software: you are free to change and redistribute it.\n");
/*
* 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
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);
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);
}
/*
* 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
#include <sys/stat.h>
#include <pwd.h>
#include <stdarg.h>
+#include <sys/time.h>
/* Global variables */
bool print_version = false;
bool auto_save = false;
bool interactive = false;
+bool timing = false;
/* History file is ~/.augeas/history */
char *history_file = NULL;
}
#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)
}
__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",
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);
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' },
{ "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}
};
flags |= AUG_SAVE_NEWFILE;
break;
case 'h':
- usage();
+ help();
break;
case 'r':
root = optarg;
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;
}
}
goto error;
fprintf(stderr, "augtool %s <http://augeas.net/>\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, " <http://www.gnu.org/licenses/lgpl-2.1.html>\n");
fprintf(stderr, "This is free software: you are free to change and redistribute it.\n");
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;
continue;
}
- code = run_command(line);
+ code = run_command(line, timing);
if (code == -2) {
free(line);
return ret;
}
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;
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)
/*
* 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
/*
* 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
/*
* 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
/*
* 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
int *nsnum = NULL;
int *nsind = NULL;
int result = -1;
+ unsigned int nstates = 0;
+ int nsigma = 0;
F(determinize(fa, NULL));
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);
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;
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;
FREE(s);
*upv = result;
if (result != NULL)
- *upv_len = strlen(result);
+ *upv_len = result_len;
return ret;
error:
FREE(result);
/*
* 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
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);
*/
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 */
* 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);
* 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);
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
#include <errno.h>
#include <assert.h>
#include <locale.h>
+#include <stdint.h>
/*
* Various parameters about env vars, special tree nodes etc.
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;
};
/*
* 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
/*
* 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
/*
* 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
* 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;
assert(slash != NULL);
v = eq + 1;
+ if (indent > 0)
+ size += indent + 1;
size += 6; /* Surrounding braces */
if (k != eq)
size += 1 + (eq - k) + 1;
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, "\"");
r = stpcpy(r, "\"");
}
r = stpcpy(r, " }");
+ if (indent > 0)
+ *r++ = '\n';
k = slash + 1;
}
return result;
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;
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;
}
/*
* 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
* { 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);
/*
* 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
/*
* 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
/*
* 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
/*
* 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
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;
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)
{
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) {
}
}
-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;
}
/*
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;
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
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))
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;
}
*************************************************************************/
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) {
while (t != t->parent && t != tree)
t = t->parent;
if (t == tree)
- ns_remove(ns, i);
+ ns_remove(ns, i, 1);
else
i += 1;
}
/*
* 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
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",
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
* 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
/*
- * 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
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 "$@"
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 \
lens-cpanel.sh \
lens-cron.sh \
lens-crypttab.sh \
+ lens-csv.sh \
lens-cyrus_imapd.sh \
lens-cups.sh \
lens-darkice.sh \
lens-lvm.sh \
lens-mailscanner.sh \
lens-mailscanner_rules.sh \
+ lens-masterpasswd.sh \
lens-mcollective.sh \
lens-mdadm_conf.sh \
lens-memcached.sh \
lens-redis.sh \
lens-reprepro_uploaders.sh \
lens-resolv.sh \
+ lens-rhsm.sh \
lens-rmt.sh \
lens-rsyncd.sh \
lens-rsyslog.sh \
lens-ssh.sh \
lens-sshd.sh \
lens-sssd.sh \
+ lens-star.sh \
lens-stunnel.sh \
lens-subversion.sh \
lens-sysconfig.sh \
lens-sysctl.sh \
lens-systemd.sh \
lens-thttpd.sh \
+ lens-tmpfiles.sh \
+ lens-trapperkeeper.sh \
lens-tuned.sh \
lens-up2date.sh \
lens-updatedb.sh \
lens-grub.sh \
lens-schroot.sh \
lens-xendconfsxp.sh \
+ lens-yaml.sh \
lens-yum.sh
ME = tests/Makefile.am
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" \
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)
/*
* 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
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);
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;
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);
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
--- /dev/null
+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
+++ /dev/null
-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
/*
* 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
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);
}
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();
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)
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
/*
* 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
--- /dev/null
+/*
+ * 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 <lutter@redhat.com>
+ */
+
+#include <config.h>
+
+#include "augeas.h"
+
+#include "cutest.h"
+#include "internal.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+
+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:
+ */
/*
* 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
/*
* 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
* 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;
/*
* 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
/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
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