James Valleroy <jvalleroy@mailbox.org>
Pavel Chechetin <pchechetin@mirantis.com>
Pedro Valero Mejia <pedro.valero.mejia@gmail.com>
+ David Farrell <davidpfarrell+github@gmail.com>
+ Nathan Ward <nward@braintrust.co.nz>
+1.12.0 - 2019-04-13
+ - General changes/additions
+ * update gnulib to 91584ed6
+ - Lens changes/additions
+ * Anaconda: new lens to process /etc/sysconfig/anaconda instead of
+ Shellvars (Pino Toscano) (Issue #597)
+ * DevfsRules: add lens for FreeBSD devfs.rules files
+ * Dovecot: permit ! in block titles (Nathan Ward) (Issue #599)
+ * Hostname: Allow creation of hostname when file is missing
+ * (David Farrell) (Issue #606)
+ * Krb5: add more pkinit_* options (Issue #603)
+ * Logrotate: fix missing recognition of double quoted filenames (Issue #611)
+ * Multipath: accept values enclosed in quotes (Issue #583)
+ * Nginx: support unix sockets as server address (Issue #618)
+ * Nsswitch: add merge action (Issue #609)
+ * Pam: accept continuation lines (Issue #590)
+ * Puppetfile: allow symbols as (optional) values (Issue #619)
+ allow comments in entries (Issue #620)
+ * Rsyslog: support dynamic file paths (Issue #622)
+ treat #!/+/- as comment (arnolda, PR #595)
+ * Syslog: accept 'include' directive (Issue #486)
+ * Semanage: new lens to process /etc/selinux/semanage.conf instead of
+ Simplevars (Pino Toscano) (Issue #594)
+ * Shellvars: allow and/or in @if conditions (#582)
+ accept functions wrapped in round brackets,
+ accept variables with a dash in their name,
+ exclude csh/tcsh profile scripts (Pino Toscano) (Issue #600)
+ accept variable as command (Issue #601)
+ * Ssh: accept RekeyLimit (Issue #605)
+ * Sshd: accept '=' to separate option names from their values
+ (Emil Dragu, #587)
+ * Sudoers: support 'always_query_group_plugin' flag (Steve Traylen, #588)
+ * Strongswan: parse lists. This is a backwards-incompatible change
+ since list entries that were parsed into a single string
+ are now split into a list of entries (Kaarle Ritvanen)
+ * Toml: new lens to parse .toml files (PR #91)
+ * Xorg: accept empty values for options (arnolda, PR #596)
+
1.11.0 - 2018-08-24
- General changes/additions
* augmatch: add a --quiet option; make the exit status useful to tell
-AC_INIT(augeas, 1.11.0)
+AC_INIT(augeas, 1.12.0)
AC_CONFIG_SRCDIR([src/augeas.c])
AC_CONFIG_AUX_DIR([build/ac-aux])
AM_CONFIG_HEADER([config.h])
-AM_INIT_AUTOMAKE([-Wno-portability 1.11 color-tests parallel-tests])
+AM_INIT_AUTOMAKE([-Wno-portability color-tests parallel-tests])
AM_SILENT_RULES([yes]) # make --enable-silent-rules the default.
fi
dnl Version info in libtool's notation
-AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:1:24])
-AC_SUBST([LIBFA_VERSION_INFO], [6:2:5])
+AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:2:24])
+AC_SUBST([LIBFA_VERSION_INFO], [6:3:5])
AC_GNU_SOURCE
File: Rtadvd (rtadvd.aug)
File: Strongswan (strongswan.aug)
File: Termcap (termcap.aug)
+ File: Anaconda (anaconda.aug)
+ File: Semanage (semanage.aug)
+ File: Toml (toml.aug)
} # Group: Specific Modules
Group: Generic Modules {
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)
+ File: Test_Anaconda (tests/test_anaconda.aug)
+ File: Test_Semanage (tests/test_semanage.aug)
+ File: test_toml.aug (tests/test_toml.aug)
} # Group: Tests and Examples
Group: Index {
-GNULIB= ../gnulib/lib/libgnu.la
-GNULIB_CFLAGS= -I $(top_srcdir)/gnulib/lib
-
-AM_CFLAGS = @AUGEAS_CFLAGS@ @WARN_CFLAGS@ @LIBXML_CFLAGS@ $(GNULIB_CFLAGS) \
+AM_CFLAGS = @AUGEAS_CFLAGS@ @WARN_CFLAGS@ @LIBXML_CFLAGS@ \
-I $(top_srcdir)/src
bin_PROGRAMS = fadot
noinst_PROGRAMS = dump
fadot_SOURCES = fadot.c
-fadot_LDADD = $(top_builddir)/src/libfa.la $(GNULIB)
+fadot_LDADD = $(top_builddir)/src/libfa.la
dump_sources = dump.c
-dump_LDADD = $(top_builddir)/src/libaugeas.la $(top_builddir)/src/libfa.la \
- $(GNULIB)
+dump_LDADD = $(top_builddir)/src/libaugeas.la $(top_builddir)/src/libfa.la
* dump '//descendant::*'
*
*/
-#define _GNU_SOURCE
-#include "config.h"
-#include "augeas.h"
+#include <augeas.h>
#include <stdio.h>
#include <stdlib.h>
* The purpose of this example is to show the usage of libfa
*/
-#include <config.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
-#include <getopt.h>
#include <stdlib.h>
#include <limits.h>
-#include "fa.h"
+#include <fa.h>
#define UCHAR_NUM (UCHAR_MAX+1)
--- /dev/null
+(*
+Module: Anaconda
+ Parses Anaconda's user interaction configuration files.
+
+Author: Pino Toscano <ptoscano@redhat.com>
+
+About: Reference
+ https://anaconda-installer.readthedocs.io/en/latest/user-interaction-config-file-spec.html
+
+About: Configuration file
+ This lens applies to /etc/sysconfig/anaconda.
+
+About: License
+ This file is licensed under the LGPL v2+, like the rest of Augeas.
+*)
+module Anaconda =
+autoload xfm
+
+let comment = IniFile.comment "#" "#"
+let sep = IniFile.sep "=" "="
+
+let entry = IniFile.entry 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/sysconfig/anaconda"
+
+let xfm = transform lns filter
--- /dev/null
+module DevfsRules =
+
+ autoload xfm
+
+ let comment = IniFile.comment IniFile.comment_re "#"
+
+ let eol = Util.eol
+
+ let line_re = /[^][#; \t\n][^#;\n]*[^#; \t\n]/
+ let entry = [ seq "entry" . store line_re . (eol | comment) ]
+
+ let title = Util.del_str "["
+ . key Rx.word . [ label "id" . Sep.equal . store Rx.integer ]
+ . Util.del_str "]" . eol
+ . counter "entry"
+
+ let record = IniFile.record title (entry | comment)
+
+ let lns = IniFile.lns record comment
+
+ let filter = incl "/etc/defaults/devfs.rules"
+ . incl "/etc/devfs.rules"
+
+ let xfm = transform lns filter
Map block enclosed in brackets recursively.
Block may be indented and have optional argument.
Block body may have entries, comments, empty lines, and nested blocks recursively. *)
-let rec block = [ indent . key block_names . (Sep.space . Quote.do_dquote_opt (store /[\/A-Za-z0-9_-]+/))? . block_newlines (entry|block|mailbox) comment . eol ]
+let rec block = [ indent . key block_names . (Sep.space . Quote.do_dquote_opt (store /!?[\/A-Za-z0-9_-]+/))? . block_newlines (entry|block|mailbox) comment . eol ]
(******************************************************************
autoload xfm
(* View: lns *)
-let lns = [ label "hostname" . store Rx.word . Util.eol ]
+let lns = [ label "hostname" . store Rx.word . Util.eol ] | Util.empty
(* View: filter *)
let filter = incl "/etc/hostname"
let realms =
let simple_option = /kdc|admin_server|database_module|default_domain/
|/v4_realm|auth_to_local(_names)?|master_kdc|kpasswd_server/
- |/admin_server|ticket_lifetime|pkinit_anchors|krb524_server/ in
+ |/admin_server|ticket_lifetime|pkinit_(anchors|identities|identity|pool)/
+ |/krb524_server/ in
let subsec_option = /v4_instance_convert/ in
let option = subsec_entry simple_option eq comment in
let subsec = [ indent . key subsec_option . eq_openbr .
let eol = Util.eol
let num = Rx.relinteger
let word = /[^,#= \n\t{}]+/
- let filename = /\/[^,#= \n\t{}]+/
+ let filename = Quote.do_quote_opt (store /\/[^"',#= \n\t{}]+/)
let size = num . /[kMG]?/
let indent = del Rx.opt_space "\t"
Util.comment
let rule =
- let filename_entry = [ label "file" . store filename ] in
+ let filename_entry = [ label "file" . filename ] in
let filename_sep = del /[ \t\n]+/ " " in
let filenames = Build.opt_list filename_entry filename_sep in
[ label "rule" . Util.indent . filenames . body . eol ]
let obr = del /\{([ \t]*)\n/ "{\n"
let cbr = del /[ \t]*}[ \t]*\n/ "}\n"
+(* Like Rx.fspath, but we disallow quotes at the beginning or end *)
+let fspath = /[^" \t\n]|[^" \t\n][^ \t\n]*[^" \t\n]/
+
let ikey (k:regexp) = indent . key k
let section (n:regexp) (b:lens) =
[ ikey n . ws . obr . (b|empty|comment)* . cbr ]
let kv (k:regexp) (v:regexp) =
- [ ikey k . ws . store v . eol ]
+ [ ikey k . ws . del /"?/ "" . store v . del /"?/ "" . eol ]
(* FIXME: it would be much more concise to write *)
(* [ key k . ws . (bare | quoted) ] *)
|qstr /(getuid|prio)_callout/
(* Settings not documented in `man multipath.conf` *)
|kv /rr_min_io_rq/ Rx.integer
- |kv "udev_dir" Rx.fspath
+ |kv "udev_dir" fspath
|qstr "selector"
|kv "async_timeout" Rx.integer
|kv "pg_timeout" Rx.word
common_setting
|kv "polling_interval" Rx.integer
|kv "max_polling_interval" Rx.integer
- |kv "multipath_dir" Rx.fspath
+ |kv "multipath_dir" fspath
|kv "find_multipaths" /yes|no/
|kv "verbosity" /[0-6]/
|kv "reassign_maps" /yes|no/
|kv "fast_io_fail_tmo" (Rx.integer|"off")
|kv "dev_loss_tmo" (Rx.integer|"infinity")
|kv "queue_without_daemon" /yes|no/
- |kv "bindings_file" Rx.fspath
- |kv "wwids_file" Rx.fspath
+ |kv "bindings_file" fspath
+ |kv "wwids_file" fspath
|kv "log_checker_err" /once|always/
|kv "retain_attached_hw_handler" /yes|no/
|kv "detect_prio" /yes|no/
|kv "hw_str_match" /yes|no/
|kv "force_sync" /yes|no/
- |kv "config_dir" Rx.fspath
+ |kv "config_dir" fspath
|kv "missing_uev_wait_timeout" Rx.integer
|kv "ignore_new_boot_devs" /yes|no/
|kv "retrigger_tries" Rx.integer
(* View: server
A simple server entry *)
let server =
- [ Util.indent . label "@server" . Util.del_str "server"
- . [ Sep.space . label "@address" . store word ]
+ let address = /[A-Za-z0-9_.:\/-]+/
+ in [ Util.indent . label "@server" . Util.del_str "server"
+ . [ Sep.space . label "@address" . store address ]
. [ Sep.space . key word . (Sep.equal . store word)? ]*
. Sep.semicolon
. (Util.eol|Util.comment_eol) ]
| /[Tt][Rr][Yy][Aa][Gg][Aa][Ii][Nn]/
in let action_kw = /[Rr][Ee][Tt][Uu][Rr][Nn]/
| /[Cc][Oo][Nn][Tt][Ii][Nn][Uu][Ee]/
+ | /[Mm][Ee][Rr][Gg][Ee]/
in let negate = [ Util.del_str "!" . label "negate" ]
in let reaction_entry = [ label "status" . negate?
. store status_kw
let eol = Util.eol
let indent = Util.indent
+ let space = del /([ \t]|\\\\\n)+/ " "
(* For the control syntax of [key=value ..] we could split the key value *)
(* pairs into an array and generate a subtree control/N/KEY = VALUE *)
- let control = /(\[[^]#\n]*\]|[^[ \t][^ \t]*)/
- let word = /[^# \t\n]+/
+ (* The valid control values if the [...] syntax is not used, is *)
+ (* required|requisite|optional|sufficient|include|substack *)
+ (* We allow more than that because this list is not case sensitive and *)
+ (* to be more lenient with typos *)
+ let control = /(\[[^]#\n]*\]|[a-zA-Z]+)/
+ let word = /([^# \t\n\\]|\\\\.)+/
(* Allowed types *)
let types = /(auth|session|account|password)/i
(* This isn't entirely right: arguments enclosed in [ .. ] can contain *)
(* a ']' if escaped with a '\' and can be on multiple lines ('\') *)
- let argument = /(\[[^]#\n]+\]|[^[#\n \t][^#\n \t]*)/
+ let argument = /(\[[^]#\n]+\]|[^[#\n \t\\][^#\n \t\\]*)/
let comment = Util.comment
let comment_or_eol = Util.comment_or_eol
(* @include module *)
(* quite a bit *)
let include = [ indent . Util.del_str "@" . key "include" .
- Util.del_ws_spc . store word . eol ]
+ space . store word . eol ]
(* Shared with PamConf *)
let record = [ label "optional" . del "-" "-" ]? .
[ label "type" . store types ] .
- Sep.space .
+ space .
[ label "control" . store control] .
- Sep.space .
+ space .
[ label "module" . store word ] .
- [ Sep.space . label "argument" . store argument ]* .
+ [ space . label "argument" . store argument ]* .
comment_or_eol
let record_svc = [ seq "record" . indent . record ]
(* View: comma
a comma, optionally preceded or followed by spaces or newlines *)
let comma = del /[ \t\n]*,[ \t\n]*/ ", "
+let comma_nospace = del /[ \t\n]*,/ ","
+
+let comment_or_eol = Util.eol | Util.comment_eol
+let quote_to_comment_or_eol = Quote.do_quote (store /[^#\n]*/) . comment_or_eol
(* 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 ]
+let moduledir = [ Util.indent . key "moduledir" . Sep.space
+ . quote_to_comment_or_eol ]
(* View: forge
a forge entry *)
-let forge = [ Util.indent . key "forge" . Sep.space . Quote.any . Util.eol ]
+let forge = [ Util.indent . key "forge" . Sep.space
+ . quote_to_comment_or_eol ]
(* View: metadata
a metadata entry *)
-let metadata = [ Util.indent . key "metadata" . Util.eol ]
+let metadata = [ Util.indent . key "metadata" . comment_or_eol ]
(* View: mod
a module entry, with optional version and options *)
let mod =
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]*/) ]
- in let opts = Build.opt_list opt comma
+ in let version = [ label "@version" . Quote.do_quote (store /[^#:\n]+/) . Util.comment_eol? ]
+ in let sto_opt_val = store /[^#"', \t\n][^#"',\n]*[^#"', \t\n]|[^#"', \t\n]/
+ in let opt = [
+ Util.del_str ":" . key Rx.word
+ . (del /[ \t]*=>[ \t]*/ " => " . Quote.do_quote_opt sto_opt_val)?
+ ]
+ in let opt_eol = del /([ \t\n]*\n)?/ ""
+ in let opt_space_or_eol = del /[ \t\n]*/ " "
+ in let comma_opt_eol_comment = comma_nospace . (opt_eol . Util.comment_eol)*
+ . opt_space_or_eol
+ in let opts = Build.opt_list opt comma_opt_eol_comment
in [ Util.indent . Util.del_str "mod" . seq "mod" . Sep.space . mod_name
- . (comma . version)?
- . (comma . opts)?
- . Util.eol ]
+ . (comma_opt_eol_comment . version)?
+ . (comma_opt_eol_comment . opts . Util.comment_eol?)?
+ . Util.eol ]
(* View: lns
the Puppetfile lens *)
File action with a specified template *)
let file_tmpl = Syslog.file . [ label "template" . Util.del_str ";" . store Rx.word ]
+let dynamic = [ Util.del_str "?" . label "dynamic" . store Rx.word ]
+
let namedpipe = Syslog.pipe . Sep.space . [ label "pipe" . store Syslog.file_r ]
-let action = Syslog.action | omusrmsg | file_tmpl | namedpipe
+let action = Syslog.action | omusrmsg | file_tmpl | dynamic | namedpipe
+
+(* Cannot use syslog program because rsyslog does not suppport #! *)
+let program = [ label "program" . Syslog.bang .
+ ( Syslog.opt_plus | [ Build.xchgs "-" "reverse" ] ) .
+ Syslog.programs . Util.eol . Syslog.entries ]
+
+(* Cannot use syslog hostname because rsyslog does not suppport #+/- *)
+let hostname = [ label "hostname" .
+ ( Syslog.plus | [ Build.xchgs "-" "reverse" ] ) .
+ Syslog.hostnames . Util.eol . Syslog.entries ]
(* View: entry
An entry contains selectors and an action
in [ label "filter" . prop_name . sep . prop_oper . sep . prop_val .
Sep.space . prop_act . Util.eol ]
-let entries = ( Syslog.empty | Syslog.comment | entry | macro | config_object | prop_filter )*
+let entries = ( Syslog.empty | Util.comment | entry | macro | config_object | prop_filter )*
-let lns = entries . ( Syslog.program | Syslog.hostname )*
+let lns = entries . ( program | hostname )*
let filter = incl "/etc/rsyslog.conf"
. incl "/etc/rsyslog.d/*"
--- /dev/null
+(*
+Module: Semanage
+ Parses /etc/selinux/semanage.conf
+
+Author:
+ Pino Toscano <ptoscano@redhat.com>
+
+About: License
+ This file is licenced under the LGPL v2+, like the rest of Augeas.
+
+About: Configuration files
+ This lens applies to /etc/selinux/semanage.conf. See <filter>.
+
+About: Examples
+ The <Test_Semanage> file contains various examples and tests.
+*)
+
+module Semanage =
+ autoload xfm
+
+let comment = IniFile.comment "#" "#"
+let sep = IniFile.sep "=" "="
+let empty = IniFile.empty
+let eol = IniFile.eol
+
+let entry = IniFile.entry IniFile.entry_re sep comment
+ | empty
+
+let title = IniFile.title_label "@group" (IniFile.record_re - /^end$/)
+let record = [ title . entry+ . Util.del_str "[end]" . eol ]
+
+let lns = (entry | record)*
+
+(* Variable: filter *)
+let filter = incl "/etc/selinux/semanage.conf"
+
+let xfm = transform lns filter
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 key_re = /[A-Za-z0-9_][-A-Za-z0-9_]*(\[[0-9A-Za-z_,]+\])?/ - ("unset" | "export")
let matching_re = "${!" . key_re . /[\*@]\}/
let eq = Util.del_str "="
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 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
let generic_cond_start (start_kw:string) (lbl:string)
(then_kw:string) (contents:lens) =
keyword_label start_kw lbl . Sep.space
- . sto_to_semicol . semicol_eol
+ . sto_to_semicol
+ . ( action_and sto_to_semicol | action_or sto_to_semicol )*
+ . semicol_eol
. keyword then_kw . eol
. contents
. entry+
. Util.indent . Util.del_str "}" . eol ]
- let function (entry:lens) =
+ let function (entry:lens) (start_kw:string) (end_kw:string) =
[ Util.indent . label "@function"
. del /(function[ \t]+)?/ ""
. store Rx.word . del /[ \t]*\(\)/ "()"
- . (comment_eol|brace_eol) . Util.del_str "{" . brace_eol
+ . (comment_eol|brace_eol) . Util.del_str start_kw . brace_eol
. entry+
- . Util.indent . Util.del_str "}" . eol ]
+ . Util.indent . Util.del_str end_kw . eol ]
let rec rec_entry =
let entry = comment | entry_eol | rec_entry in
| loop_while entry
| loop_until entry
| case entry entry_noeol
- | function entry
+ | function entry "{" "}"
+ | function entry "(" ")"
| subshell entry
let lns_norec = del_empty* . (comment | entry_eol) *
let filter_sysconfig =
sc_incl "*" .
+ sc_excl "anaconda" .
sc_excl "bootloader" .
sc_excl "hw-uuid" .
sc_excl "hwconf" .
. excl "/etc/default/whoopsie"
. incl "/etc/profile"
. incl "/etc/profile.d/*"
+ . excl "/etc/profile.d/*.csh"
+ . excl "/etc/profile.d/csh.local"
let filter_misc = incl "/etc/arno-iptables-firewall/debconf.cfg"
. incl "/etc/conf.d/*"
. incl "/etc/cron-apt/config"
. incl "/etc/audit/auditd.conf"
. incl "/etc/mixerctl.conf"
. incl "/etc/wsconsctlctl.conf"
- . incl "/etc/selinux/semanage.conf"
let xfm = transform lns filter
let global_knownhosts_file = spaces_entry /GlobalKnownHostsFile/i
+ let rekey_limit = [ indent . key /RekeyLimit/i . spc_eq .
+ [ label "amount" . value_to_spc ] .
+ [ spc . label "duration" . value_to_spc ]? . eol ]
+
let special_entry = send_env
| proxy_command
| remote_fw
| ciphers
| algorithms
| pubkey_accepted_key_types
- | global_knownhosts_file
+ | global_knownhosts_file
+ | rekey_limit
let key_re = /[A-Za-z0-9]+/
- - /SendEnv|Host|ProxyCommand|RemoteForward|LocalForward|MACs|Ciphers|(HostKey|Kex)Algorithms|PubkeyAcceptedKeyTypes|GlobalKnownHostsFile/i
+ - /SendEnv|Host|ProxyCommand|RemoteForward|LocalForward|MACs|Ciphers|(HostKey|Kex)Algorithms|PubkeyAcceptedKeyTypes|GlobalKnownHostsFile|RekeyLimit/i
let other_entry = [ indent . key key_re
let eol = del /[ \t]*\n/ "\n"
- let sep = Util.del_ws_spc
+ let sep = del /[ \t=]+/ " "
let indent = del /[ \t]*/ " "
let empty = Util.empty
let array_entry (kw:regexp) (sq:string) =
- let bare = Quote.do_quote_opt_nil (store /[^"' \t\n]+/) in
+ let bare = Quote.do_quote_opt_nil (store /[^"' \t\n=]+/) in
let quoted = Quote.do_quote (store /[^"'\n]*[ \t]+[^"'\n]*/) in
[ key kw
. ( [ sep . seq sq . bare ] | [ sep . seq sq . quoted ] )*
. eol ]
let other_entry =
- let value = store /[^ \t\n]+([ \t]+[^ \t\n]+)*/ in
+ let value = store /[^ \t\n=]+([ \t=]+[^ \t\n=]+)*/ in
[ key key_re . sep . value . eol ]
let accept_env = array_entry /AcceptEnv/i "AcceptEnv"
let deny_users = array_entry /DenyUsers/i "DenyUsers"
let subsystemvalue =
- let value = store (/[^ \t\n](.*[^ \t\n])?/) in
+ let value = store (/[^ \t\n=](.*[^ \t\n=])?/) in
[ key /[A-Za-z0-9\-]+/ . sep . value . eol ]
let subsystem =
[ key /Subsystem/i . sep . subsystemvalue ]
let list (kw:regexp) (sq:string) =
- let value = store /[^, \t\n]+/ in
+ let value = store /[^, \t\n=]+/ in
[ key kw . sep .
[ seq sq . value ] .
([ seq sq . Util.del_str "," . value])* .
| other_entry
let condition_entry =
- let value = store /[^ \t\n]+/ in
+ let value = store /[^ \t\n=]+/ in
[ sep . key /[A-Za-z0-9]+/ . sep . value ]
let match_cond =
Kaarle Ritvanen <kaarle.ritvanen@datakunkku.fi>
About: Reference
- strongswan.conf(5)
+ strongswan.conf(5), swanctl.conf(5)
About: License
This file is licensed under the LGPL v2+
let ws = del /[\n\t ]*(#[\t ]*\n[\n\t ]*)*/
let rec conf =
- let name (sep:string) =
- key (/[^\/.\{\}#\n\t ]+/ - /include/) . Util.del_ws_spc .
- Util.del_str sep
+ let keys = /[^\/.\{\}#\n\t ]+/ - /include/
+ in let lists = /(crl|oscp)_uris|(local|remote)_(addrs|ts)|vips|pools|(ca)?certs|pubkeys|groups|cert_policy|dns|nbns|dhcp|netmask|server|subnet|split_(in|ex)clude|interfaces_(ignore|use)|preferred/
+ in let proposals = /((ah|esp)_)?proposals/
+ in let name (pat:lens) (sep:string) =
+ pat . Util.del_ws_spc . Util.del_str sep
in let val = store /[^\n\t ].*/ . Util.del_str "\n" . ws ""
in let sval = Util.del_ws_spc . val
+ in let ival (pat:lens) (end:string) =
+ Util.del_opt_ws " " . seq "item" . pat . Util.del_str end
+ in let list (l:string) (k:regexp) (v:lens) =
+ [ label l . name (store k) "=" . counter "item" .
+ [ ival v "," ]* . [ ival v "\n" ] . ws "" ]
+ in let alg = seq "alg" . store /[a-z0-9]+/
in (
[ Util.del_str "#" . label "#comment" . Util.del_opt_ws " " . val ] |
[ key "include" . sval ] |
- [ name "=" . sval ] |
- [ name "{" . ws "\n" . conf . Util.del_str "}" . ws "\n" ]
+ [ name (key (keys - lists - proposals)) "=" . sval ] |
+ list "#list" lists (store /[^\n\t ,][^\n,]*/) |
+ list "#proposals" proposals (counter "alg" . [ alg ] . [ Util.del_str "-" . alg ]*) |
+ [ name (key keys) "{" . ws "\n" . conf . Util.del_str "}" . ws "\n" ]
)*
let lns = ws "" . conf
| "closefrom_override" | "compress_io" | "fast_glob"
| "log_input" | "log_output" | "pwfeedback"
| "umask_override" | "use_pty" | "match_group_by_gid"
+ | "always_query_group_plugin"
let parameter_flag = [ del_negate . negate_node?
. key parameter_flag_kw ]
(* View: entries
entries are either comments/empty lines or entries
*)
- let entries = (empty | comment | entry)*
+ let entries = (empty | comment | entry )*
(* Group: Program matching *)
(* Group: Top of the tree *)
+ let include =
+ [ key "include" . sep_tab . store file_r . eol ]
+
(* View: lns
generic entries then programs or hostnames matching blocs
*)
- let lns = entries . ( program | hostname )*
+ let lns = entries . ( program | hostname | include )*
(* Variable: filter
all you need is /etc/syslog.conf
--- /dev/null
+(*
+Module: Test_Anaconda
+ Provides unit tests and examples for the <Anaconda> lens.
+
+ - 'exampleN' snippets are taken from the documentation:
+ https://anaconda-installer.readthedocs.io/en/latest/user-interaction-config-file-spec.html
+ - 'installedN' snippets are taken from the resulting files after
+ a successful installation
+*)
+
+module Test_Anaconda =
+
+let example1 = "# comment example - before the section headers
+
+[section_1]
+# comment example - inside section 1
+key_a_in_section1=some_value
+key_b_in_section1=some_value
+
+[section_2]
+# comment example - inside section 2
+key_a_in_section2=some_value
+"
+
+test Anaconda.lns get example1 =
+ { "#comment" = "comment example - before the section headers" }
+ { }
+ { "section_1"
+ { "#comment" = "comment example - inside section 1" }
+ { "key_a_in_section1" = "some_value" }
+ { "key_b_in_section1" = "some_value" }
+ { }
+ }
+ { "section_2"
+ { "#comment" = "comment example - inside section 2" }
+ { "key_a_in_section2" = "some_value" }
+ }
+
+let example2 = "# this is the user interaction config file
+
+[General]
+post_install_tools_disabled=0
+
+[DatetimeSpoke]
+# the date and time spoke has been visited
+visited=1
+changed_timezone=1
+changed_ntp=0
+changed_timedate=1
+
+[KeyboardSpoke]
+# the keyboard spoke has not been visited
+visited=0
+"
+
+test Anaconda.lns get example2 =
+ { "#comment" = "this is the user interaction config file" }
+ { }
+ { "General"
+ { "post_install_tools_disabled" = "0" }
+ { }
+ }
+ { "DatetimeSpoke"
+ { "#comment" = "the date and time spoke has been visited" }
+ { "visited" = "1" }
+ { "changed_timezone" = "1" }
+ { "changed_ntp" = "0" }
+ { "changed_timedate" = "1" }
+ { }
+ }
+ { "KeyboardSpoke"
+ { "#comment" = "the keyboard spoke has not been visited" }
+ { "visited" = "0" }
+ }
+
+let installed1 = "# This file has been generated by the Anaconda Installer 21.48.22.134-1
+
+[ProgressSpoke]
+visited = 1
+
+"
+
+test Anaconda.lns get installed1 =
+ { "#comment" = "This file has been generated by the Anaconda Installer 21.48.22.134-1" }
+ { }
+ { "ProgressSpoke"
+ { "visited" = "1" }
+ { }
+ }
--- /dev/null
+module Test_DevfsRules =
+
+ let manpage_example = "[localrules=10]
+add path 'da*s*' mode 0660 group usb
+"
+
+ test DevfsRules.lns get manpage_example =
+ { "localrules" { "id" = "10" }
+ { "1" = "add path 'da*s*' mode 0660 group usb" } }
+
+
+ let example = "[devfsrules_jail_unhide_usb_printer_and_scanner=30]
+add include $devfsrules_hide_all
+add include $devfsrules_unhide_basic
+add include $devfsrules_unhide_login
+add path 'ulpt*' mode 0660 group printscan unhide
+add path 'unlpt*' mode 0660 group printscan unhide
+add path 'ugen2.8' mode 0660 group printscan unhide # Scanner (ugen2.8 is a symlink to usb/2.8.0)
+add path usb unhide
+add path usbctl unhide
+add path 'usb/2.8.0' mode 0660 group printscan unhide
+
+[devfsrules_jail_unhide_usb_scanner_only=30]
+add include $devfsrules_hide_all
+add include $devfsrules_unhide_basic
+add include $devfsrules_unhide_login
+add path 'ugen2.8' mode 0660 group scan unhide # Scanner
+add path usb unhide
+add path usbctl unhide
+add path 'usb/2.8.0' mode 0660 group scan unhide
+"
+
+ test DevfsRules.lns get example =
+ { "devfsrules_jail_unhide_usb_printer_and_scanner" { "id" = "30" }
+ { "1" = "add include $devfsrules_hide_all" }
+ { "2" = "add include $devfsrules_unhide_basic" }
+ { "3" = "add include $devfsrules_unhide_login" }
+ { "4" = "add path 'ulpt*' mode 0660 group printscan unhide" }
+ { "5" = "add path 'unlpt*' mode 0660 group printscan unhide" }
+ { "6" = "add path 'ugen2.8' mode 0660 group printscan unhide"
+ { "#comment" = "Scanner (ugen2.8 is a symlink to usb/2.8.0)" }
+ }
+ { "7" = "add path usb unhide" }
+ { "8" = "add path usbctl unhide" }
+ { "9" = "add path 'usb/2.8.0' mode 0660 group printscan unhide" }
+ { }
+ }
+ { "devfsrules_jail_unhide_usb_scanner_only" { "id" = "30" }
+ { "1" = "add include $devfsrules_hide_all" }
+ { "2" = "add include $devfsrules_unhide_basic" }
+ { "3" = "add include $devfsrules_unhide_login" }
+ { "4" = "add path 'ugen2.8' mode 0660 group scan unhide"
+ { "#comment" = "Scanner" }
+ }
+ { "5" = "add path usb unhide" }
+ { "6" = "add path usbctl unhide" }
+ { "7" = "add path 'usb/2.8.0' mode 0660 group scan unhide" }
+ }
+
mail_attachment_min_size = 128k
mail_attachment_fs = sis posix
mail_attachment_hash = %{sha1}
+
+protocol !indexer-worker {
+ mail_vsize_bg_after_count = 0
+}
"
test Dovecot.lns get mail_conf =
{ "#comment" = "# Mailbox locations and namespaces" }
{ "mail_attachment_min_size" = "128k" }
{ "mail_attachment_fs" = "sis posix" }
{ "mail_attachment_hash" = "%{sha1}" }
+ { }
+ { "protocol" = "!indexer-worker"
+ { "mail_vsize_bg_after_count" = "0" }
+ }
(* ********************************* master ********************************* *)
{ "file" = "/var/log/bar" }
{ "schedule" = "monthly" } }
+test Logrotate.rule get "\"/var/log/foo\"\n{\n monthly\n}\n" =
+ { "rule"
+ { "file" = "/var/log/foo" }
+ { "schedule" = "monthly" } }
+
let conf = "# see man logrotate for details
# rotate log files weekly
weekly
{ "device"
{ "vendor" = "SomeCorp" }
{ "product" = "2.5\"\" SSD" } } }
+
+(* Issue #583 - allow optional quotes around values and strip them *)
+test Multipath.lns get "devices {
+ device {
+ vendor \"COMPELNT\"
+ product \"Compellent Vol\"
+ path_grouping_policy \"multibus\"
+ path_checker \"tur\"
+ features \"0\"
+ hardware_handler \"0\"
+ prio \"const\"
+ failback \"immediate\"
+ rr_weight \"uniform\"
+ no_path_retry \"queue\"
+ }
+}\n" =
+ { "devices"
+ { "device"
+ { "vendor" = "COMPELNT" }
+ { "product" = "Compellent Vol" }
+ { "path_grouping_policy" = "multibus" }
+ { "path_checker" = "tur" }
+ { "features" = "0" }
+ { "hardware_handler" = "0" }
+ { "prio" = "const" }
+ { "failback" = "immediate" }
+ { "rr_weight" = "uniform" }
+ { "no_path_retry" = "queue" } } }
{ "root" = "/var/www/html" }
{ "internal"
{ "#comment" = "only valid in location blocks" } } }
+
+test lns get "upstream php-handler {
+ server unix:/var/run/php/php7.3-fpm.sock;
+}\n" =
+ { "upstream"
+ { "#name" = "php-handler" }
+ { "@server"
+ { "@address" = "unix:/var/run/php/php7.3-fpm.sock" } } }
+
{ "argument" = "[motd=/etc/bad example]" }
}
+(* Multiline PAM entries; issue #590 *)
+test Pam.lns get "account \\\ninclude \\\n system-auth\n" =
+ { "1"
+ { "type" = "account" }
+ { "control" = "include" }
+ { "module" = "system-auth" } }
+
+test Pam.lns get "account\\\n[success=1 default=ignore] \\
+ pam_succeed_if.so\\\nuser\\\n =\\\nvagrant\\\nuse_uid\\\nquiet\n" =
+ { "1"
+ { "type" = "account" }
+ { "control" = "[success=1 default=ignore]" }
+ { "module" = "pam_succeed_if.so" }
+ { "argument" = "user" }
+ { "argument" = "=" }
+ { "argument" = "vagrant" }
+ { "argument" = "use_uid" }
+ { "argument" = "quiet" } }
+
(* Local Variables: *)
(* mode: caml *)
(* End: *)
module Test_Puppetfile =
(* Test: Puppetfile.lns *)
-test Puppetfile.lns get "forge \"https://forgeapi.puppetlabs.com\"
+test Puppetfile.lns get "forge \"https://forgeapi.puppetlabs.com\" # the default forge
mod 'puppetlabs-razor'
mod 'puppetlabs-ntp', \"0.0.3\"
mod 'puppetlabs-apache', '0.6.0',
:github_tarball => 'puppetlabs/puppetlabs-apache'
-metadata\n" =
- { "forge" = "https://forgeapi.puppetlabs.com" }
+metadata # we want metadata\n" =
+ { "forge" = "https://forgeapi.puppetlabs.com"
+ { "#comment" = "the default forge" } }
{ }
{ "1" = "puppetlabs-razor" }
{ "2" = "puppetlabs-ntp"
{ "github_tarball" = "puppetlabs/puppetlabs-apache" }
}
{ }
- { "metadata" }
+ { "metadata" { "#comment" = "we want metadata" } }
(* Test: Puppetfile.lns
Complex version conditions *)
:git => \"git://github.com/puppetlabs/puppetlabs-stdlib.git\"\n" =
{ "1" = "stdlib"
{ "git" = "git://github.com/puppetlabs/puppetlabs-stdlib.git" } }
+
+
+(* Issue #427 *)
+test Puppetfile.lns get "mod 'puppetlabs/apache', :latest\n" =
+ { "1" = "puppetlabs/apache"
+ { "latest" } }
+
+test Puppetfile.lns get "mod 'data',
+ :git => 'ssh://git@stash.example.com/bp/puppet-hiera.git',
+ :branch => :control_branch,
+ :default_branch => 'development',
+ :install_path => '.'\n" =
+ { "1" = "data"
+ { "git" = "ssh://git@stash.example.com/bp/puppet-hiera.git" }
+ { "branch" = ":control_branch" }
+ { "default_branch" = "development" }
+ { "install_path" = "." } }
+
+(* Comment: after module name comma
+ This conflicts with the comma comment tree below *)
+test Puppetfile.lns get "mod 'data' # eol comment\n" = *
+
+(* Comment: after first comma *)
+test Puppetfile.lns get "mod 'data', # eol comment
+ # and another
+ '1.2.3'\n" =
+ { "1" = "data"
+ { "#comment" = "eol comment" }
+ { "#comment" = "and another" }
+ { "@version" = "1.2.3" } }
+
+(* Comment: after version
+ Current culprit: need two \n *)
+test Puppetfile.lns get "mod 'data', '1.2.3' # eol comment\n" = *
+test Puppetfile.lns get "mod 'data', '1.2.3' # eol comment\n\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" { "#comment" = "eol comment" } } }
+
+(* Comment: eol after version comma *)
+test Puppetfile.lns get "mod 'data', '1.2.3', # a comment
+ :local => true\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" }
+ { "#comment" = "a comment" }
+ { "local" = "true" } }
+
+(* Comment: after version comma with newline *)
+test Puppetfile.lns get "mod 'data', '1.2.3',
+ # a comment
+ :local => true\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" }
+ { "#comment" = "a comment" }
+ { "local" = "true" } }
+
+(* Comment: eol before opts, without version *)
+test Puppetfile.lns get "mod 'data', # a comment
+ # :ref => 'abcdef',
+ :local => true\n" =
+ { "1" = "data"
+ { "#comment" = "a comment" }
+ { "#comment" = ":ref => 'abcdef'," }
+ { "local" = "true" } }
+
+(* Comment: after opt comma *)
+test Puppetfile.lns get "mod 'data', '1.2.3',
+ :ref => 'abcdef', # eol comment
+ :local => true\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" }
+ { "ref" = "abcdef" }
+ { "#comment" = "eol comment" }
+ { "local" = "true" } }
+
+(* Comment: in opts *)
+test Puppetfile.lns get "mod 'data', '1.2.3',
+ :ref => 'abcdef',
+ # a comment
+ :local => true\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" }
+ { "ref" = "abcdef" }
+ { "#comment" = "a comment" }
+ { "local" = "true" } }
+
+(* Comment: after last opt *)
+test Puppetfile.lns get "mod 'data', '1.2.3',
+ :local => true # eol comment\n\n" =
+ { "1" = "data"
+ { "@version" = "1.2.3" }
+ { "local" = "true" }
+ { "#comment" = "eol comment" } }
{ "#comment" = "Turn off message reception via local log socket;" } }
{ "#comment" = "local messages are retrieved through imjournal now." }
+(* rsyslog doesn't use bsd-like #! or #+/- specifications *)
+test Rsyslog.lns get "#!prog\n" = { "#comment" = "!prog" }
+test Rsyslog.lns get "#+host\n" = { "#comment" = "+host" }
+test Rsyslog.lns get "#-host\n" = { "#comment" = "-host" }
+
(* Added in rsyslog 8.33 *)
test Rsyslog.lns get "include(file=\"/etc/rsyslog.d/*.conf\" mode=\"optional\")\n" =
{ "include"
{ "file" = "/etc/rsyslog.d/*.conf" }
{ "mode" = "optional" } }
+
+(* Dynamic file name template *)
+test Rsyslog.lns get "*.* ?DynamicFile\n" =
+ { "entry"
+ { "selector"
+ { "facility" = "*" }
+ { "level" = "*" }
+ }
+ { "action"
+ { "dynamic" = "DynamicFile" }
+ }
+ }
--- /dev/null
+(*
+Module: Test_Semanage
+ Provides unit tests and examples for the <Semanage> lens.
+*)
+
+module Test_Semanage =
+
+(* Variable: phony_conf *)
+let phony_conf = "# this is a comment
+
+mykey = myvalue # eol comment
+anotherkey = another value
+"
+
+(* Test: Semanage.lns *)
+test Semanage.lns get phony_conf =
+ { "#comment" = "this is a comment" }
+ { }
+ { "mykey" = "myvalue"
+ { "#comment" = "eol comment" } }
+ { "anotherkey" = "another value" }
+
+(* Test: Semanage.lns
+ Quotes are OK in variables that do not begin with a quote *)
+test Semanage.lns get "UserParameter=custom.vfs.dev.read.ops[*],cat /proc/diskstats | grep $1 | head -1 | awk '{print $$4}'\n" =
+ { "UserParameter" = "custom.vfs.dev.read.ops[*],cat /proc/diskstats | grep $1 | head -1 | awk '{print $$4}'" }
+
+(* Test: Semanage.lns
+ Support empty values *)
+test Semanage.lns get "foo =\n" =
+ { "foo" }
+
+(* Variable: conf *)
+let conf = "module-store = direct
+module-store = \"source\"
+
+#policy-version = 19
+
+expand-check=0
+
+usepasswd=False
+bzip-small=true
+bzip-blocksize=5
+ignoredirs=/root
+
+[sefcontext_compile]
+path = /usr/sbin/sefcontext_compile
+args = -r $@
+
+[end]
+
+config=test
+
+[verify module]
+test=value
+[end]
+"
+
+(* Test: Semanage.lns *)
+test Semanage.lns get conf =
+ { "module-store" = "direct" }
+ { "module-store" = "source" }
+ { }
+ { "#comment" = "policy-version = 19" }
+ { }
+ { "expand-check" = "0" }
+ { }
+ { "usepasswd" = "False" }
+ { "bzip-small" = "true" }
+ { "bzip-blocksize" = "5" }
+ { "ignoredirs" = "/root" }
+ { }
+ { "@group" = "sefcontext_compile"
+ { "path" = "/usr/sbin/sefcontext_compile" }
+ { "args" = "-r $@" }
+ { } }
+ { }
+ { "config" = "test" }
+ { }
+ { "@group" = "verify module"
+ { "test" = "value" } }
{ ".source" = "/tmp/bar" }
}
+ test Shellvars.lns get "foo() (
+ . /tmp/bar
+ )\n" =
+ { "@function" = "foo"
+ { ".source" = "/tmp/bar" }
+ }
+
(* Dollar assignment *)
test Shellvars.lns get "FOO=$(bar arg)\n" =
{ "FOO" = "$(bar arg)" }
(* Make sure to support empty comments *)
test lns get "# foo
- #
+ #
#
foo=bar
#\n" =
test lns get "alias ls='ls $LS_OPTIONS'\n" =
{ "@alias" = "ls" { "value" = "'ls $LS_OPTIONS'" } }
+ test lns get "alias ls-options='ls $LS_OPTIONS'\n" =
+ { "@alias" = "ls-options" { "value" = "'ls $LS_OPTIONS'" } }
+
(* Allow && and || constructs after condition *)
test Shellvars.lns get "[ -f $FILENAME ] && do this || or that\n" =
{ "@condition" = "-f $FILENAME"
(* Test: Shellvars.lns
Parse (almost) any command *)
test Shellvars.lns get "echo foobar 'and this is baz'
-/usr/local/bin/myscript.sh with args
+/usr/local/bin/myscript-with-dash_and_underscore.sh with args
echo foo \
bar\n" =
{ "@command" = "echo"
{ "@arg" = "foobar 'and this is baz'" }
}
- { "@command" = "/usr/local/bin/myscript.sh"
+ { "@command" = "/usr/local/bin/myscript-with-dash_and_underscore.sh"
{ "@arg" = "with args" }
}
{ "@command" = "echo"
}\n" =
{ "@subshell"
{ "@command" = "echo" }
- }
+ }
(* One-liner function *)
test Shellvars.lns get "MyFunc() { echo; }\n" =
{ "@command" = "echo" }
}
+(* Support and/or in if conditions *)
+test Shellvars.lns get "if [ -f /tmp/file1 ] && [ -f /tmp/file2 ] || [ -f /tmp/file3 ]; then
+ echo foo
+fi
+" =
+ { "@if" = "[ -f /tmp/file1 ]"
+ { "@and" = "[ -f /tmp/file2 ]" }
+ { "@or" = "[ -f /tmp/file3 ]" }
+ { "@command" = "echo"
+ { "@arg" = "foo" }
+ }
+ }
+
+(* Support variable as command *)
+test Shellvars.lns get "$FOO bar\n" =
+ { "@command" = "$FOO"
+ { "@arg" = "bar" }
+ }
+
(*********************************************************
* Group: Unsupported syntax *
test Ssh.lns get "ForwardAgent =\tyes\n" =
{ "ForwardAgent" = "yes" }
+
+(* Issue #605 *)
+test Ssh.lns get "RekeyLimit 1G 1h\n" =
+ { "RekeyLimit"
+ { "amount" = "1G" }
+ { "duration" = "1h" } }
+
+test Ssh.lns get "RekeyLimit 1G\n" =
+ { "RekeyLimit"
+ { "amount" = "1G" } }
{ "foo" { "bar" = "baz" } }
{ "quux" }
{ "#comment" = "quuux" }
+
+
+let connection = "
+
+connections {
+ foo {
+ pools = bar, baz
+ proposals = aes256gcm16-aes128gcm16-ecp512, aes256-sha256-sha1-ecp256-modp4096-modp2048, 3des-md5-modp768
+ }
+ children {
+ bar {
+ esp_proposals = aes128-sha256-sha1,3des-md5
+ }
+ }
+}
+"
+
+test Strongswan.lns get connection =
+ { "connections"
+ { "foo"
+ { "#list" = "pools"
+ { "1" = "bar" }
+ { "2" = "baz" }
+ }
+ { "#proposals" = "proposals"
+ { "1"
+ { "1" = "aes256gcm16" }
+ { "2" = "aes128gcm16" }
+ { "3" = "ecp512" }
+ }
+ { "2"
+ { "1" = "aes256" }
+ { "2" = "sha256" }
+ { "3" = "sha1" }
+ { "4" = "ecp256" }
+ { "5" = "modp4096" }
+ { "6" = "modp2048" }
+ }
+ { "3"
+ { "1" = "3des" }
+ { "2" = "md5" }
+ { "3" = "modp768" }
+ }
+ }
+ }
+ { "children"
+ { "bar"
+ { "#proposals" = "esp_proposals"
+ { "1"
+ { "1" = "aes128" }
+ { "2" = "sha256" }
+ { "3" = "sha1" }
+ }
+ { "2"
+ { "1" = "3des" }
+ { "2" = "md5" }
+ }
+ }
+ }
+ }
+ }
(* allow space before comments *)
test Syslog.lns get " \t# space comment\n" =
{ "#comment" = "space comment" }
+
+ test Syslog.lns get "include /etc/syslog.d\n" =
+ { "include" = "/etc/syslog.d" }
--- /dev/null
+module Test_Toml =
+
+(* Test: Toml.norec
+ String value *)
+test Toml.norec get "\"foo\"" = { "string" = "foo" }
+
+(* Test: Toml.norec
+ Integer value *)
+test Toml.norec get "42" = { "integer" = "42" }
+
+(* Test: Toml.norec
+ Positive integer value *)
+test Toml.norec get "+42" = { "integer" = "+42" }
+
+(* Test: Toml.norec
+ Negative integer value *)
+test Toml.norec get "-42" = { "integer" = "-42" }
+
+(* Test: Toml.norec
+ Large integer value *)
+test Toml.norec get "5_349_221" = { "integer" = "5_349_221" }
+
+(* Test: Toml.norec
+ Hexadecimal integer value *)
+test Toml.norec get "0xDEADBEEF" = { "integer" = "0xDEADBEEF" }
+
+(* Test: Toml.norec
+ Octal integer value *)
+test Toml.norec get "0o755" = { "integer" = "0o755" }
+
+(* Test: Toml.norec
+ Binary integer value *)
+test Toml.norec get "0b11010110" = { "integer" = "0b11010110" }
+
+(* Test: Toml.norec
+ Float value *)
+test Toml.norec get "3.14" = { "float" = "3.14" }
+
+(* Test: Toml.norec
+ Positive float value *)
+test Toml.norec get "+3.14" = { "float" = "+3.14" }
+
+(* Test: Toml.norec
+ Negative float value *)
+test Toml.norec get "-3.14" = { "float" = "-3.14" }
+
+(* Test: Toml.norec
+ Complex float value *)
+test Toml.norec get "-3_220.145_223e-34" = { "float" = "-3_220.145_223e-34" }
+
+(* Test: Toml.norec
+ Inf float value *)
+test Toml.norec get "-inf" = { "float" = "-inf" }
+
+(* Test: Toml.norec
+ Nan float value *)
+test Toml.norec get "-nan" = { "float" = "-nan" }
+
+(* Test: Toml.norec
+ Bool value *)
+test Toml.norec get "true" = { "bool" = "true" }
+
+(* Test: Toml.norec
+ Datetime value *)
+test Toml.norec get "1979-05-27T07:32:00Z" =
+ { "datetime" = "1979-05-27T07:32:00Z" }
+test Toml.norec get "1979-05-27 07:32:00.999999" =
+ { "datetime" = "1979-05-27 07:32:00.999999" }
+
+(* Test: Toml.norec
+ Date value *)
+test Toml.norec get "1979-05-27" =
+ { "date" = "1979-05-27" }
+
+(* Test: Toml.norec
+ Time value *)
+test Toml.norec get "07:32:00" =
+ { "time" = "07:32:00" }
+
+(* Test: Toml.norec
+ String value with newline *)
+test Toml.norec get "\"bar\nbaz\"" =
+ { "string" = "bar\nbaz" }
+
+(* Test: Toml.norec
+ Multiline value *)
+test Toml.norec get "\"\"\"\nbar\nbaz\n \"\"\"" =
+ { "string_multi" = "bar\nbaz" }
+
+(* Test: Toml.norec
+ Literal string value *)
+test Toml.norec get "'bar\nbaz'" =
+ { "string_literal" = "bar\nbaz" }
+
+(* Test: Toml.array_norec
+ Empty array *)
+test Toml.array_norec get "[ ]" =
+ { "array" {} }
+
+(* Test: Toml.array_norec
+ Array of strings *)
+test Toml.array_norec get "[ \"foo\", \"bar\" ]" =
+ { "array" {}
+ { "string" = "foo" } {}
+ { "string" = "bar" } {} }
+
+(* Test: Toml.array_norec
+ Array of arrays *)
+test Toml.array_rec get "[ [ \"foo\", [ 42, 43 ] ], \"bar\" ]" =
+ { "array" {}
+ { "array" {}
+ { "string" = "foo" } {}
+ { "array" {}
+ { "integer" = "42" } {}
+ { "integer" = "43" } {} } {} } {}
+ { "string" = "bar" } {} }
+
+(* Test: Toml.lns
+ Global parameters *)
+test Toml.lns get "# Globals
+foo = \"bar\"\n" =
+ { "#comment" = "Globals" }
+ { "entry" = "foo" { "string" = "bar" } }
+
+(* Test: Toml.lns
+ Simple section/value *)
+test Toml.lns get "[foo]
+bar = \"baz\"\n" =
+ { "table" = "foo" { "entry" = "bar" { "string" = "baz" } } }
+
+(* Test: Toml.lns
+ Subsections *)
+test Toml.lns get "[foo]
+title = \"bar\"
+ [foo.one]
+ hello = \"world\"\n" =
+ { "table" = "foo"
+ { "entry" = "title" { "string" = "bar" } } }
+ { "table" = "foo.one"
+ { "entry" = "hello" { "string" = "world" } } }
+
+(* Test: Toml.lns
+ Nested subsections *)
+test Toml.lns get "[foo]
+[foo.one]
+[foo.one.two]
+bar = \"baz\"\n" =
+ { "table" = "foo" }
+ { "table" = "foo.one" }
+ { "table" = "foo.one.two"
+ { "entry" = "bar" { "string" = "baz" } } }
+
+(* Test: Toml.lns
+ Arrays of tables *)
+test Toml.lns get "[[products]]
+name = \"Hammer\"
+sku = 738594937
+
+[[products]]
+
+[[products]]
+name = \"Nail\"
+sku = 284758393
+color = \"gray\"\n" =
+ { "@table" = "products"
+ { "entry" = "name"
+ { "string" = "Hammer" } }
+ { "entry" = "sku"
+ { "integer" = "738594937" } }
+ { }
+ }
+ { "@table" = "products"
+ { }
+ }
+ { "@table" = "products"
+ { "entry" = "name"
+ { "string" = "Nail" } }
+ { "entry" = "sku"
+ { "integer" = "284758393" } }
+ { "entry" = "color"
+ { "string" = "gray" } }
+ }
+
+(* Test: Toml.entry
+ Empty inline table *)
+test Toml.entry get "name = { }\n" =
+ { "entry" = "name"
+ { "inline_table"
+ { } } }
+
+(* Test: Toml.entry
+ Inline table *)
+test Toml.entry get "name = { first = \"Tom\", last = \"Preston-Werner\" }\n" =
+ { "entry" = "name"
+ { "inline_table" {}
+ { "entry" = "first"
+ { "string" = "Tom" } } {}
+ { "entry" = "last"
+ { "string" = "Preston-Werner" } } {} } }
+
+(* Variable: example
+ The example from https://github.com/mojombo/toml *)
+let example = "# This is a TOML document. Boom.
+
+title = \"TOML Example\"
+
+[owner]
+name = \"Tom Preston-Werner\"
+organization = \"GitHub\"
+bio = \"GitHub Cofounder & CEO\nLikes tater tots and beer.\"
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = \"192.168.1.1\"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = \"10.0.0.1\"
+ dc = \"eqdc10\"
+
+ [servers.beta]
+ ip = \"10.0.0.2\"
+ dc = \"eqdc10\"
+ country = \"中国\" # This should be parsed as UTF-8
+
+[clients]
+data = [ [\"gamma\", \"delta\"], [1, 2] ] # just an update to make sure parsers support it
+
+# Line breaks are OK when inside arrays
+hosts = [
+ \"alpha\",
+ \"omega\"
+]
+
+# Products
+
+ [[products]]
+ name = \"Hammer\"
+ sku = 738594937
+
+ [[products]]
+ name = \"Nail\"
+ sku = 284758393
+ color = \"gray\"
+"
+
+test Toml.lns get example =
+ { "#comment" = "This is a TOML document. Boom." } { }
+ { "entry" = "title"
+ { "string" = "TOML Example" } } { }
+ { "table" = "owner"
+ { "entry" = "name"
+ { "string" = "Tom Preston-Werner" } }
+ { "entry" = "organization"
+ { "string" = "GitHub" } }
+ { "entry" = "bio"
+ { "string" = "GitHub Cofounder & CEO
+Likes tater tots and beer." }
+ }
+ { "entry" = "dob"
+ { "datetime" = "1979-05-27T07:32:00Z" }
+ { "#comment" = "First class dates? Why not?" } } { } }
+ { "table" = "database"
+ { "entry" = "server"
+ { "string" = "192.168.1.1" } }
+ { "entry" = "ports"
+ { "array" { }
+ { "integer" = "8001" } { }
+ { "integer" = "8001" } { }
+ { "integer" = "8002" } { } } }
+ { "entry" = "connection_max"
+ { "integer" = "5000" } }
+ { "entry" = "enabled"
+ { "bool" = "true" } } { } }
+ { "table" = "servers" { }
+ { "#comment" = "You can indent as you please. Tabs or spaces. TOML don't care." } }
+ { "table" = "servers.alpha"
+ { "entry" = "ip"
+ { "string" = "10.0.0.1" } }
+ { "entry" = "dc"
+ { "string" = "eqdc10" } } { } }
+ { "table" = "servers.beta"
+ { "entry" = "ip"
+ { "string" = "10.0.0.2" } }
+ { "entry" = "dc"
+ { "string" = "eqdc10" } }
+ { "entry" = "country"
+ { "string" = "中国" }
+ { "#comment" = "This should be parsed as UTF-8" } } { } }
+ { "table" = "clients"
+ { "entry" = "data"
+ { "array" { }
+ { "array"
+ { "string" = "gamma" } { }
+ { "string" = "delta" } } { }
+ { "array"
+ { "integer" = "1" } { }
+ { "integer" = "2" } } { } }
+ { "#comment" = "just an update to make sure parsers support it" } } { }
+ { "#comment" = "Line breaks are OK when inside arrays" }
+ { "entry" = "hosts"
+ { "array" { }
+ { "string" = "alpha" } { }
+ { "string" = "omega" } { } } } { }
+ { "#comment" = "Products" } { } }
+ { "@table" = "products"
+ { "entry" = "name"
+ { "string" = "Hammer" } }
+ { "entry" = "sku"
+ { "integer" = "738594937" } } { } }
+ { "@table" = "products"
+ { "entry" = "name"
+ { "string" = "Nail" } }
+ { "entry" = "sku"
+ { "integer" = "284758393" } }
+ { "entry" = "color"
+ { "string" = "gray" } } }
+
Option \"MonitorLayout\" \"LVDS,VGA\"
VideoRam 229376
Option \"NoAccel\"
+ Option \"fbdev\" \"\"
Screen 0
EndSection
{ "Option" = "MonitorLayout"
{ "value" = "LVDS,VGA" } }
{ "VideoRam" = "229376" }
- { "Option" = "NoAccel" }
+ { "Option" = "NoAccel" }
+ { "Option" = "fbdev"
+ { "value" = "" } }
{ "Screen"
{ "num" = "0" } } }
{ }
--- /dev/null
+(*
+Module: Toml
+ Parses TOML files
+
+Author: Raphael Pinson <raphael.pinson@camptocamp.com>
+
+About: Reference
+ https://github.com/mojombo/toml/blob/master/README.md
+
+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 TOML files.
+
+About: Examples
+ The <Test_Toml> file contains various examples and tests.
+*)
+
+module Toml =
+
+(* Group: base definitions *)
+
+(* View: comment
+ A simple comment *)
+let comment = IniFile.comment "#" "#"
+
+(* View: empty
+ An empty line *)
+let empty = Util.empty_dos
+
+(* View: eol
+ An end of line *)
+let eol = Util.doseol
+
+
+(* Group: value entries *)
+
+let bare_re_noquot = (/[^][", \t\r\n]/ - "#")
+let bare_re = (/[^][,\r=]/ - "#")+
+let no_quot = /[^]["\r\n]*/
+let bare = Quote.do_dquote_opt_nil (store (bare_re_noquot . (bare_re* . bare_re_noquot)?))
+let quoted = Quote.do_dquote (store (/[^"]/ . "#"* . /[^"]/))
+
+let ws = del /[ \t\n]*/ ""
+
+let space_or_empty = [ del /[ \t\n]+/ " " ]
+
+let comma = Util.del_str "," . (space_or_empty | comment)?
+let lbrace = Util.del_str "{" . (space_or_empty | comment)?
+let rbrace = Util.del_str "}"
+let lbrack = Util.del_str "[" . (space_or_empty | comment)?
+let rbrack = Util.del_str "]"
+
+(* This follows the definition of 'string' at https://www.json.org/
+ It's a little wider than what's allowed there as it would accept
+ nonsensical \u escapes *)
+let triple_dquote = Util.del_str "\"\"\""
+let str_store = Quote.dquote . store /([^\\"]|\\\\["\/bfnrtu\\])*/ . Quote.dquote
+
+let str_store_multi = triple_dquote . eol
+ . store /([^\\"]|\\\\["\/bfnrtu\\])*/
+ . del /\n[ \t]*/ "\n" . triple_dquote
+
+let str_store_literal = Quote.squote . store /([^\\']|\\\\['\/bfnrtu\\])*/ . Quote.squote
+
+let integer =
+ let base10 = /[+-]?[0-9_]+/
+ in let hex = /0x[A-Za-z0-9]+/
+ in let oct = /0o[0-7]+/
+ in let bin = /0b[01]+/
+ in [ label "integer" . store (base10 | hex | oct | bin) ]
+
+let float =
+ let n = /[0-9_]+/
+ in let pm = /[+-]?/
+ in let z = pm . n
+ in let decim = "." . n
+ in let exp = /[Ee]/ . z
+ in let num = z . decim | z . exp | z . decim . exp
+ in let inf = pm . "inf"
+ in let nan = pm . "nan"
+ in [ label "float" . store (num | inf | nan) ]
+
+let str = [ label "string" . str_store ]
+
+let str_multi = [ label "string_multi" . str_store_multi ]
+
+let str_literal = [ label "string_literal" . str_store_literal ]
+
+let bool (r:regexp) = [ label "bool" . store r ]
+
+
+let date_re = /[0-9]{4}-[0-9]{2}-[0-9]{2}/
+let time_re = /[0-9]{1,2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?[A-Z]*/
+
+let datetime = [ label "datetime" . store (date_re . /[T ]/ . time_re) ]
+let date = [ label "date" . store date_re ]
+let time = [ label "time" . store time_re ]
+
+let norec = str | str_multi | str_literal
+ | integer | float | bool /true|false/
+ | datetime | date | time
+
+let array (value:lens) = [ label "array" . lbrack
+ . ( ( Build.opt_list value comma . space_or_empty? . rbrack )
+ | rbrack ) ]
+
+let array_norec = array norec
+
+let rec array_rec = array (norec | array_rec)
+
+let entry_base (value:lens) = [ label "entry" . store Rx.word . Sep.space_equal . value ]
+
+let inline_table (value:lens) = [ label "inline_table" . lbrace
+ . ( (Build.opt_list (entry_base value) comma . space_or_empty? . rbrace)
+ | rbrace ) ]
+
+let entry = [ label "entry" . Util.indent . store Rx.word . Sep.space_equal
+ . (norec | array_rec | inline_table norec) . (eol | comment) ]
+
+(* Group: tables *)
+
+(* View: table_gen
+ A generic table *)
+let table_gen (name:string) (lbrack:string) (rbrack:string) =
+ let title = Util.indent . label name
+ . Util.del_str lbrack
+ . store /[^]\r\n.]+(\.[^]\r\n.]+)*/
+ . Util.del_str rbrack . eol
+ in [ title . (entry|empty|comment)* ]
+
+(* View: table
+ A table or array of tables *)
+let table = table_gen "table" "[" "]"
+ | table_gen "@table" "[[" "]]"
+
+(* Group: lens *)
+
+(* View: lns
+ The Toml lens *)
+let lns = (entry | empty | comment)* . table*
(* Variable: generic_entry_re *)
let generic_entry_re = /[^# \t\n\/]+/ - entries_re
+(* Variable: quoted_non_empty_string_val *)
+let quoted_non_empty_string_val = del "\"" "\"" . store /[^"\n]+/
+ . del "\"" "\""
+ (* " relax, emacs *)
+
(* Variable: quoted_string_val *)
-let quoted_string_val = del "\"" "\"" . store /[^"\n]+/ . del "\"" "\""
+let quoted_string_val = del "\"" "\"" . store /[^"\n]*/ . del "\"" "\""
(* " relax, emacs *)
(* Variable: int *)
*)
let entry_str (canon:string) (re:regexp) =
[ indent . del re canon . label canon
- . sep_spc . quoted_string_val . eol ]
+ . sep_spc . quoted_non_empty_string_val . eol ]
(* View: entry_generic
* An entry without a specific handler. Store everything after the keyword,
(* View: option *)
let option = [ indent . del /[oO]ption/ "Option" . label "Option" . sep_spc
- . quoted_string_val
+ . quoted_non_empty_string_val
. [ label "value" . sep_spc . quoted_string_val ]*
. eol ]
*)
let screen = [ indent . del /[sS]creen/ "Screen" . label "Screen"
. [ sep_spc . label "num" . store int ]?
- . ( sep_spc . quoted_string_val
+ . ( sep_spc . quoted_non_empty_string_val
. [ sep_spc . label "position" . store to_eol ]? )?
. eol ]
(* View: input_device *)
let input_device = [ indent . del /[iI]nput[dD]evice/ "InputDevice"
- . label "InputDevice" . sep_spc . quoted_string_val
- . [ label "option" . sep_spc . quoted_string_val ]*
+ . label "InputDevice" . sep_spc
+ . quoted_non_empty_string_val
+ . [ label "option" . sep_spc
+ . quoted_non_empty_string_val ]*
. eol ]
(* View: driver *)
(* View: display_modes *)
let display_modes = [ indent . del /[mM]odes/ "Modes" . label "Modes"
- . [ label "mode" . sep_spc . quoted_string_val ]+
+ . [ label "mode" . sep_spc
+ . quoted_non_empty_string_val ]+
. eol ]
(*************************************************************************
* Use ROOT as the filesystem root. If ROOT is NULL, use the value of the
* environment variable AUGEAS_ROOT. If that doesn't exist eitehr, use "/".
*
- * LOADPATH is a colon-spearated list of directories that modules should be
+ * LOADPATH is a colon-separated list of directories that modules should be
* searched in. This is in addition to the standard load path and the
- * directories in AUGEAS_LENS_LIB
+ * directories in AUGEAS_LENS_LIB. LOADPATH can be NULL, indicating that
+ * nothing should be added to the load path.
*
* FLAGS is a bitmask made up of values from AUG_FLAGS. The flag
* AUG_NO_ERR_CLOSE can be used to get more information on why
struct passwd pwbuf;
struct passwd *pw = NULL;
long val = sysconf(_SC_GETPW_R_SIZE_MAX);
- size_t strbuflen = val;
- if (val < 0)
- return NULL;
+ if (val < 0) {
+ // The libc won't tell us how big a buffer to reserve.
+ // Let's hope that 16k is enough (it really should be).
+ val = 16*1024;
+ }
+
+ size_t strbuflen = (size_t) val;
if (ALLOC_N(strbuf, strbuflen) < 0)
return NULL;
if (getpwuid_r(uid, &pwbuf, strbuf, strbuflen, &pw) != 0 || pw == NULL) {
free(strbuf);
+
+ // Try to get the user's home dir from the environment
+ char *env = getenv("HOME");
+ if (env != NULL) {
+ return strdup(env);
+ }
return NULL;
}
%option reentrant noyywrap
%option warn nodefault
%option outfile="lex.yy.c" prefix="augl_"
+%option noinput nounput
%top{
/* config.h must precede flex's inclusion of <stdio.h>
lens-activemq_xml.sh \
lens-afs_cellalias.sh \
lens-aliases.sh \
+ lens-anaconda.sh \
lens-anacron.sh \
lens-approx.sh \
lens-apt_update_manager.sh \
lens-darkice.sh \
lens-debctrl.sh \
lens-desktop.sh \
+ lens-devfsrules.sh \
lens-device_map.sh \
lens-dhclient.sh \
lens-dhcpd.sh \
lens-rx.sh \
lens-samba.sh \
lens-securetty.sh \
+ lens-semanage.sh \
lens-services.sh \
lens-shadow.sh \
lens-shells.sh \
lens-thttpd.sh \
lens-tmpfiles.sh \
lens-trapperkeeper.sh \
+ lens-toml.sh \
lens-tuned.sh \
lens-up2date.sh \
lens-updatedb.sh \
--- /dev/null
+# This file has been generated by the Anaconda Installer 21.48.22.134-1
+
+[ProgressSpoke]
+visited = 1
+
--- /dev/null
+# $FreeBSD$
+#
+# Spaces ARE valid field separators in this file. However,
+# other *nix-like systems still insist on using tabs as field
+# separators. If you are sharing this file between systems, you
+# may want to use only tabs as field separators here.
+# Consult the syslog.conf(5) manpage.
+*.err;kern.warning;auth.notice;mail.crit /dev/console
+*.notice;authpriv.none;kern.debug;lpr.info;mail.crit;news.err /var/log/messages
+security.* /var/log/security
+auth.info;authpriv.info /var/log/auth.log
+mail.info /var/log/maillog
+lpr.info /var/log/lpd-errs
+ftp.info /var/log/xferlog
+cron.* /var/log/cron
+!-devd
+*.=debug /var/log/debug.log
+*.emerg *
+# uncomment this to log all writes to /dev/console to /var/log/console.log
+# touch /var/log/console.log and chmod it to mode 600 before it will work
+#console.info /var/log/console.log
+# uncomment this to enable logging of all log messages to /var/log/all.log
+# touch /var/log/all.log and chmod it to mode 600 before it will work
+#*.* /var/log/all.log
+# uncomment this to enable logging to a remote loghost named loghost
+#*.* @loghost
+# uncomment these if you're running inn
+# news.crit /var/log/news/news.crit
+# news.err /var/log/news/news.err
+# news.notice /var/log/news/news.notice
+# Uncomment this if you wish to see messages produced by devd
+# !devd
+# *.>=notice /var/log/devd.log
+!ppp
+*.* /var/log/ppp.log
+!*
+include /etc/syslog.d
+include /usr/local/etc/syslog.d