From 077c19ff0a4b8ad6e965b68a2141385f048b6f3a Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Wed, 3 Jul 2019 09:21:19 +0900 Subject: [PATCH] Imported Upstream version 1.11.0 --- .travis.yml | 2 +- NEWS | 35 +++++++ configure.ac | 6 +- doc/naturaldocs/conf/c_api/Topics.txt | 2 +- doc/naturaldocs/conf/lenses/Languages.txt | 2 +- doc/naturaldocs/conf/lenses/Menu.txt | 5 + doc/naturaldocs/conf/lenses/Topics.txt | 2 +- lenses/chrony.aug | 14 ++- lenses/dhclient.aug | 14 ++- lenses/fstab.aug | 1 + lenses/grub.aug | 58 +++++++++-- lenses/httpd.aug | 2 +- lenses/json.aug | 5 +- lenses/multipath.aug | 7 +- lenses/nginx.aug | 9 +- lenses/redis.aug | 14 ++- lenses/rsyslog.aug | 2 +- lenses/strongswan.aug | 41 ++++++++ lenses/systemd.aug | 4 + lenses/tests/test_chrony.aug | 20 +++- lenses/tests/test_dhclient.aug | 11 +- lenses/tests/test_fstab.aug | 4 + lenses/tests/test_grub.aug | 25 +++++ lenses/tests/test_httpd.aug | 19 ++++ lenses/tests/test_json.aug | 16 +++ lenses/tests/test_multipath.aug | 26 ++++- lenses/tests/test_nginx.aug | 13 +++ lenses/tests/test_redis.aug | 16 ++- lenses/tests/test_rsyslog.aug | 6 ++ lenses/tests/test_strongswan.aug | 65 ++++++++++++ man/augmatch.pod | 9 ++ src/augeas.c | 2 +- src/augeas.h | 2 + src/augmatch.c | 55 +++++++--- src/augparse.c | 1 + src/augtool.c | 13 +-- src/builtin.c | 161 ++++++++++++++++++++---------- src/fa.c | 5 + src/get.c | 6 +- src/internal.h | 5 - src/jmt.c | 10 +- src/lens.c | 4 +- src/pathx.c | 57 ++++++++--- src/put.c | 8 +- src/regexp.c | 9 +- src/syntax.c | 54 ++++------ src/syntax.h | 11 +- src/transform.c | 2 +- tests/Makefile.am | 5 +- tests/test-api.c | 36 +++++++ tests/test-augmatch.sh | 54 ++++++++++ 51 files changed, 760 insertions(+), 195 deletions(-) create mode 100644 lenses/strongswan.aug create mode 100644 lenses/tests/test_strongswan.aug create mode 100755 tests/test-augmatch.sh diff --git a/.travis.yml b/.travis.yml index 3df9693..8be1d2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,4 +14,4 @@ notifications: irc: channels: - "irc.freenode.org#augeas" -script: ./configure && make && make check && ./src/try valgrind +script: ./configure && make && make check VERBOSE=1 && ./src/try valgrind diff --git a/NEWS b/NEWS index a03936b..1f42452 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,38 @@ +1.11.0 - 2018-08-24 + - General changes/additions + * augmatch: add a --quiet option; make the exit status useful to tell + whether there was a match or not + * Drastically reduce the amount of memory needed to evaluate complex + path expressions against large files (Issue #569) + * Fix a segfault on OSX when 'augmatch' is run without any + arguments (Issue #556) + - API changes + * aug_source did not in fact return the source; and always returned + NULL for that. That has been fixed. + - Lens changes/additions + * Chrony: add new options supported in chrony 3.2 and 3.3 (Miroslav Lichvar) + * Dhclient: fix parsing of append/prepend and similar directives + (John Morrissey) + * Fstab: allow leading whitespace in mount entry lines + (Pino Toscano) (Issue #544) + * Grub: tolerate some invalid entries. Those invalid entries + get mapped to '#error' nodes + * Httpd: accept comments with whitespace right after a tag + opening a section (Issue #577) + * Json: allow escaped slashes in strings (Issue #557) + * Multipath: accept regular expressions for devnode, wwid, and property + in blacklist and blacklist_exceptions sections (Issue #564) + * Nginx: parse /etc/nginx/sites-enabled (plumbeo) + allow semicolons inside double quoted strings in + simple directives, and allow simple directives without + an argument (Issue #566) + * Redis: accept the 'bind' statement with multiple IP addresses + (yannh) (Issue #194) + * Rsyslog: support include() directive introduced in rsyslog 8.33 + * Strongswan: new lens (Kaarle Ritvanen) + * Systemd: do not try to treat *.d or *.wants directories as + configuration files (Issue #548) + 1.10.1 - 2018-01-29 - API changes * Fix a symbol versioning mistake in libfa that unnecessarily broke ABI diff --git a/configure.ac b/configure.ac index 41c8eb1..11d7ade 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(augeas, 1.10.1) +AC_INIT(augeas, 1.11.0) AC_CONFIG_SRCDIR([src/augeas.c]) AC_CONFIG_AUX_DIR([build/ac-aux]) AM_CONFIG_HEADER([config.h]) @@ -65,8 +65,8 @@ if test x"$enable_debug" = x"yes"; then fi dnl Version info in libtool's notation -AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:0:24]) -AC_SUBST([LIBFA_VERSION_INFO], [6:1:5]) +AC_SUBST([LIBAUGEAS_VERSION_INFO], [24:1:24]) +AC_SUBST([LIBFA_VERSION_INFO], [6:2:5]) AC_GNU_SOURCE diff --git a/doc/naturaldocs/conf/c_api/Topics.txt b/doc/naturaldocs/conf/c_api/Topics.txt index 3a45bb3..905270f 100644 --- a/doc/naturaldocs/conf/c_api/Topics.txt +++ b/doc/naturaldocs/conf/c_api/Topics.txt @@ -24,7 +24,7 @@ Format: 1.52 # Alter Topic Type: [name] # Creates a new topic type or alters one from the main file. Each type gets # its own index and behavior settings. Its name can have letters, numbers, -# spaces, and these characters: - / . ' +# spaces, and these charaters: - / . ' # # Plural: [name] # Sets the plural name of the topic type, if different. diff --git a/doc/naturaldocs/conf/lenses/Languages.txt b/doc/naturaldocs/conf/lenses/Languages.txt index 32fbe4e..47a9622 100644 --- a/doc/naturaldocs/conf/lenses/Languages.txt +++ b/doc/naturaldocs/conf/lenses/Languages.txt @@ -117,7 +117,7 @@ Language: Augeas Extension: aug Block Comment: (* *) + Augeas Test Prototype Enders: \n let test module filter Augeas Lens Prototype Enders: \n let test module filter Augeas Variable Prototype Enders: \n let test module filter - Augeas Test Prototype Enders: \n let test module filter Perl Package: NaturalDocs::Languages::Augeas diff --git a/doc/naturaldocs/conf/lenses/Menu.txt b/doc/naturaldocs/conf/lenses/Menu.txt index a41b530..190907f 100644 --- a/doc/naturaldocs/conf/lenses/Menu.txt +++ b/doc/naturaldocs/conf/lenses/Menu.txt @@ -204,6 +204,11 @@ Group: Specific Modules { File: Yaml (yaml.aug) File: Cron_User (cron_user.aug) File: Oz (oz.aug) + File: Getcap (getcap.aug) + File: Rancid (rancid.aug) + File: Rtadvd (rtadvd.aug) + File: Strongswan (strongswan.aug) + File: Termcap (termcap.aug) } # Group: Specific Modules Group: Generic Modules { diff --git a/doc/naturaldocs/conf/lenses/Topics.txt b/doc/naturaldocs/conf/lenses/Topics.txt index c0657ec..7054ff0 100644 --- a/doc/naturaldocs/conf/lenses/Topics.txt +++ b/doc/naturaldocs/conf/lenses/Topics.txt @@ -17,7 +17,7 @@ Ignore Keywords: # Alter Topic Type: [name] # Creates a new topic type or alters one from the main file. Each type gets # its own index and behavior settings. Its name can have letters, numbers, -# spaces, and these characters: - / . ' +# spaces, and these charaters: - / . ' # # Plural: [name] # Sets the plural name of the topic type, if different. diff --git a/lenses/chrony.aug b/lenses/chrony.aug index 2792ced..9ce177b 100644 --- a/lenses/chrony.aug +++ b/lenses/chrony.aug @@ -78,11 +78,13 @@ module Chrony = (* Variable: cmd_options Server/Peer/Pool options with values *) - let cmd_options = "key" + let cmd_options = "asymmetry" + | "key" | /maxdelay((dev)?ratio)?/ | /(min|max)poll/ | /(min|max)samples/ | "maxsources" + | "mindelay" | "offset" | "polltarget" | "port" @@ -93,7 +95,7 @@ module Chrony = Server/Peer/Pool options without values *) let cmd_flags = "auto_offline"|"iburst"|"noselect"|"offline"|"prefer" - |"require"|"trust"|"xleave" + |"require"|"trust"|"xleave"|"burst" (* Variable: ntp_source Server/Peer/Pool key names @@ -109,6 +111,7 @@ module Chrony = HW timestamping options with values *) let hwtimestamp_options = "minpoll"|"precision"|"rxcomp"|"txcomp" + |"rxfilter" (* Variable: hwtimestamp_flags HW timestamping options without values @@ -135,12 +138,12 @@ module Chrony = *) let refclock_options = "refid"|"lock"|"poll"|"dpoll"|"filter"|"rate" |"minsamples"|"maxsamples"|"offset"|"delay" - |"precision"|"maxdispersion" + |"precision"|"maxdispersion"|"stratum"|"width" (* Variable: refclock_flags refclock options without values *) - let refclock_flags = "noselect"|"prefer"|"require"|"trust" + let refclock_flags = "noselect"|"pps"|"prefer"|"require"|"tai"|"trust" (* Variable: flags Options without values @@ -265,7 +268,8 @@ module Chrony = let hwtimestamp = [ Util.indent . key "hwtimestamp" . space . [ label "interface" . store no_space ] . ( space . ( [ key hwtimestamp_flags ] - | [ key hwtimestamp_options . space . store number ] ) + | [ key hwtimestamp_options . space + . store no_space ] ) )* . eol ] (* View: istepslew diff --git a/lenses/dhclient.aug b/lenses/dhclient.aug index 33e68d4..19e924c 100644 --- a/lenses/dhclient.aug +++ b/lenses/dhclient.aug @@ -93,19 +93,23 @@ let stmt_array = [ key stmt_array_re let stmt_hash_re = "send" | "option" -let stmt_hash = [ key stmt_hash_re - . sep_spc - . ( [ key word . sep_spc . sto_to_spc_noeval ] +let stmt_args = ( [ key word . sep_spc . sto_to_spc_noeval ] | [ key word . sep_spc . (rfc_code|eval) ] ) . sep_scl - . comment_or_eol ] + . comment_or_eol + +let stmt_hash = [ key stmt_hash_re + . sep_spc + . stmt_args ] let stmt_opt_mod_re = "append" | "prepend" | "default" | "supersede" -let stmt_opt_mod = [ key stmt_opt_mod_re . sep_spc . stmt_hash ] +let stmt_opt_mod = [ key stmt_opt_mod_re + . sep_spc + . stmt_args ] (************************************************************************ * BLOCK STATEMENTS diff --git a/lenses/fstab.aug b/lenses/fstab.aug index 7087c34..2e54955 100644 --- a/lenses/fstab.aug +++ b/lenses/fstab.aug @@ -23,6 +23,7 @@ module Fstab = Build.opt_list lns comma let record = [ seq "mntent" . + Util.indent . [ label "spec" . store spec ] . sep_tab . [ label "file" . store file ] . sep_tab . comma_sep_list "vfstype" . diff --git a/lenses/grub.aug b/lenses/grub.aug index 06f9e79..24ad39b 100644 --- a/lenses/grub.aug +++ b/lenses/grub.aug @@ -29,9 +29,6 @@ module Grub = (* View: eol *) let eol = Util.eol - (* View: del_to_eol *) - let del_to_eol = del /[^ \t\n]*/ "" - (* View: spc *) let spc = Util.del_ws_spc @@ -92,7 +89,22 @@ module Grub = eol ] (* View: kw_pres *) - let kw_pres (kw:string) = [ opt_ws . key kw . del_to_eol . eol ] + let kw_pres (kw:string) = [ opt_ws . key kw . eol ] + + (* View: error + * Parse a line that looks almost like a valid setting, but isn't, + * into an '#error' node. Any line that starts with letters, but not + * anything matching kw, is considered an error line. + * + * Parameters: + * kw:regexp - the valid keywords that are _not_ considered an + * error + *) + let error (kw:regexp) = + let not_kw = /[a-zA-Z]+/ - kw in + [ label "#error" . Util.del_opt_ws "\t" + . store (not_kw . /([^a-zA-Z\n].*[^ \t\n])?/) . eol ] + (************************************************************************ * Group: BOOT ENTRIES @@ -138,8 +150,8 @@ module Grub = spc . [ label "from" . store Rx.no_spaces ] )? . eol ] - (* View: menu_setting *) - let menu_setting = kw_menu_arg "default" + (* View: menu_entry *) + let menu_entry = kw_menu_arg "default" | kw_menu_arg "fallback" | kw_pres "hiddenmenu" | kw_menu_arg "timeout" @@ -156,6 +168,21 @@ module Grub = | device | setkey + (* View: menu_error + * Accept lines not matching menu_entry and stuff them into + * '#error' nodes + *) + let menu_error = + let kw = /default|fallback|hiddenmenu|timeout|splashimage|gfxmenu/ + |/foreground|background|verbose|boot|password|title/ + |/serial|setkey|terminal|color|device/ in + error kw + + (* View: menu_setting + * a valid menu setting or a line that looks like one but is an #error + *) + let menu_setting = menu_entry | menu_error + (* View: title *) let title = del /title[ \t=]+/ "title " . value_to_eol . eol @@ -206,9 +233,9 @@ module Grub = let configfile = [ command "configfile" "\t" . spc . store Rx.no_spaces . eol ] - (* View: boot_setting + (* View: boot_entry entries *) - let boot_setting = + let boot_entry = let boot_arg_re = "root" | "initrd" | "rootnoverify" | "uuid" | "findroot" | "bootfs" (* Solaris extensions *) in kw_boot_arg boot_arg_re @@ -223,6 +250,21 @@ module Grub = | kw_pres "makeactive" | password_arg + (* View: boot_error + * Accept lines not matching boot_entry and stuff them into + * '#error' nodes + *) + let boot_error = + let kw = /lock|uuid|password|root|initrd|rootnoverify|findroot|bootfs/ + |/configfile|chainloader|title|boot|quiet|kernel|module/ + |/makeactive|savedefault|map/ in + error kw + + (* View: boot_setting + * a valid boot setting or a line that looks like one but is an #error + *) + let boot_setting = boot_entry | boot_error + (* View: boot *) let boot = let line = ((boot_setting|comment)* . boot_setting)? in diff --git a/lenses/httpd.aug b/lenses/httpd.aug index ff93a61..5600088 100644 --- a/lenses/httpd.aug +++ b/lenses/httpd.aug @@ -171,7 +171,7 @@ let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ] let section (body:lens) = (* opt_eol includes empty lines *) - let opt_eol = del /([ \t]*#?\r?\n)*/ "\n" in + let opt_eol = del /([ \t]*#?[ \t]*\r?\n)*/ "\n" in let inner = (sep_spc . argv arg_sec)? . sep_osp . dels ">" . opt_eol . ((body|comment) . (body|empty|comment)*)? . indent . dels " + +About: Reference + strongswan.conf(5) + +About: License + This file is licensed under the LGPL v2+ +*) + +module Strongswan = + +autoload xfm + +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 + in let val = store /[^\n\t ].*/ . Util.del_str "\n" . ws "" + in let sval = Util.del_ws_spc . val +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" ] +)* + +let lns = ws "" . conf + +let xfm = transform lns ( + incl "/etc/strongswan.d/*.conf" . + incl "/etc/strongswan.d/**/*.conf" . + incl "/etc/swanctl/conf.d/*.conf" . + incl "/etc/swanctl/swanctl.conf" +) diff --git a/lenses/systemd.aug b/lenses/systemd.aug index f7858e9..b868e86 100644 --- a/lenses/systemd.aug +++ b/lenses/systemd.aug @@ -172,6 +172,10 @@ let filter = incl "/lib/systemd/system/*" . incl "/etc/systemd/system/*/*" . incl "/etc/systemd/logind.conf" . incl "/etc/sysconfig/*.systemd" + . excl "/lib/systemd/system/*.d" + . excl "/etc/systemd/system/*.d" + . excl "/lib/systemd/system/*.wants" + . excl "/etc/systemd/system/*.wants" . Util.stdexcl let xfm = transform lns filter diff --git a/lenses/tests/test_chrony.aug b/lenses/tests/test_chrony.aug index ba201c2..9011caf 100644 --- a/lenses/tests/test_chrony.aug +++ b/lenses/tests/test_chrony.aug @@ -22,6 +22,7 @@ server ntp5.example.com maxdelay 2 offline server ntp6.example.com maxdelay 2 iburst presend 2 xleave offset 1e-4 server ntp7.example.com iburst presend 2 offline prefer trust require server ntp8.example.com minsamples 8 maxsamples 16 version 3 +server ntp9.example.com burst mindelay 0.1 asymmetry 0.5 peer ntpc1.example.com pool pool1.example.com iburst maxsources 3 allow @@ -53,12 +54,13 @@ mailonchange root@localhost 0.5 maxchange 1000 1 2 maxdistance 1.0 maxdrift 100 -hwtimestamp eth0 minpoll -2 txcomp 300e-9 rxcomp 645e-9 nocrossts +hwtimestamp eth0 minpoll -2 txcomp 300e-9 rxcomp 645e-9 nocrossts rxfilter all 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 SHM 0 refid SHM0 delay 0.1 offset 0.2 noselect tai stratum 3 +refclock SOCK /var/run/chrony-GPS.sock pps width 0.1 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 @@ -111,6 +113,11 @@ ntpsigndsocket /var/lib/samba/ntp_signd { "maxsamples" = "16" } { "version" = "3" } } + { "server" = "ntp9.example.com" + { "burst" } + { "mindelay" = "0.1" } + { "asymmetry" = "0.5" } + } { "peer" = "ntpc1.example.com" } { "pool" = "pool1.example.com" { "iburst" } @@ -188,6 +195,7 @@ ntpsigndsocket /var/lib/samba/ntp_signd { "txcomp" = "300e-9" } { "rxcomp" = "645e-9" } { "nocrossts" } + { "rxfilter" = "all" } } { "initstepslew" { "threshold" = "30" } @@ -211,6 +219,14 @@ ntpsigndsocket /var/lib/samba/ntp_signd { "delay" = "0.1" } { "offset" = "0.2" } { "noselect" } + { "tai" } + { "stratum" = "3" } + } + { "refclock" + { "driver" = "SOCK" } + { "parameter" = "/var/run/chrony-GPS.sock" } + { "pps" } + { "width" = "0.1" } } { "refclock" { "driver" = "PPS" } diff --git a/lenses/tests/test_dhclient.aug b/lenses/tests/test_dhclient.aug index 6bc1d75..3db8ac9 100644 --- a/lenses/tests/test_dhclient.aug +++ b/lenses/tests/test_dhclient.aug @@ -133,15 +133,12 @@ lease { { "second" = "01" } } } -test Dhclient.lns get "append option domain-name-servers 127.0.0.1;\n" = +test Dhclient.lns get "append domain-name-servers 127.0.0.1;\n" = { "append" - { "option" - { "domain-name-servers" = "127.0.0.1" } - } - } + { "domain-name-servers" = "127.0.0.1" } } -test Dhclient.lns put "" after set "/prepend/option/domain-name-servers" "127.0.0.1" = - "prepend option domain-name-servers 127.0.0.1;\n" +test Dhclient.lns put "" after set "/prepend/domain-name-servers" "127.0.0.1" = + "prepend domain-name-servers 127.0.0.1;\n" (* When = is used before the value, it's an evaluated string, see dhcp-eval *) test Dhclient.lns get "send dhcp-client-identifier = hardware;\n" = diff --git a/lenses/tests/test_fstab.aug b/lenses/tests/test_fstab.aug index fa044ae..438f619 100644 --- a/lenses/tests/test_fstab.aug +++ b/lenses/tests/test_fstab.aug @@ -11,6 +11,8 @@ module Test_fstab = { "dump" = "1" } { "passno" = "1" } } + let leading_ws = " /dev/vg00/lv00\t /\t ext3\t defaults 1 1\n" + let trailing_ws = "/dev/vg00/lv00\t /\t ext3\t defaults 1 1 \t\n" let gen_no_passno(passno:string) = @@ -60,6 +62,8 @@ module Test_fstab = test Fstab.lns get simple = simple_tree + test Fstab.lns get leading_ws = simple_tree + test Fstab.lns get trailing_ws = simple_tree test Fstab.lns get no_passno = no_passno_tree diff --git a/lenses/tests/test_grub.aug b/lenses/tests/test_grub.aug index 8a0d9f4..7565720 100644 --- a/lenses/tests/test_grub.aug +++ b/lenses/tests/test_grub.aug @@ -257,3 +257,28 @@ password --encrypted ^9^32kwzzX./3WISQ0C /boot/grub/custom.lst { "password" = "secret" { "md5" } } } + + (* Test parsing of invalid entries via menu_error *) + test Grub.lns get "default=0\ncrud=no\n" = + { "default" = "0" } + { "#error" = "crud=no" } + + (* We handle some pretty bizarre bad syntax *) + test Grub.lns get "default=0 +crud no +valid:nope +nonsense = yes +bad arg1 arg2 arg3=v\n" = + { "default" = "0" } + { "#error" = "crud no" } + { "#error" = "valid:nope" } + { "#error" = "nonsense = yes" } + { "#error" = "bad arg1 arg2 arg3=v" } + + (* Test parsing of invalid entries via boot_error *) + test Grub.lns get "title test + root (hd0,0) + crud foo\n" = + { "title" = "test" + { "root" = "(hd0,0)" } + { "#error" = "crud foo" } } diff --git a/lenses/tests/test_httpd.aug b/lenses/tests/test_httpd.aug index 1d3737b..9887f7a 100644 --- a/lenses/tests/test_httpd.aug +++ b/lenses/tests/test_httpd.aug @@ -627,3 +627,22 @@ test Httpd.directive get { "arg" = "X-Forwarded-Proto" } { "arg" = "https" } { "arg" = "expr=(%{HTTP:CF-Visitor}='{\"scheme\":\"https\"}')" } } + +(* Issue #577: we make the newline starting a section optional, including + an empty comment at the end of the line. This used to miss empty comments + with whitespace *) +test Httpd.lns get "#\n\n" = { "If" { "arg" = "cond" } } + +test Httpd.lns get "# \n\n" = { "If" { "arg" = "cond" } } + +test Httpd.lns get "\n# \n\n" = { "If" { "arg" = "cond" } } + +test Httpd.lns get "# text\n\n" = + { "If" + { "arg" = "cond" } + { "#comment" = "text" } } + +test Httpd.lns get "\n\t# text\n\n" = + { "If" + { "arg" = "cond" } + { "#comment" = "text" } } diff --git a/lenses/tests/test_json.aug b/lenses/tests/test_json.aug index cb61cef..1ad0624 100644 --- a/lenses/tests/test_json.aug +++ b/lenses/tests/test_json.aug @@ -494,3 +494,19 @@ test lns get "{ \"filesystem\": \"ext3\\\" \\\\ \t \r\n SEC_TYPE=\\\"ext2\" }\n" { "entry" = "filesystem" { "string" = "ext3\\\" \\\\ \t \r\n SEC_TYPE=\\\"ext2" } } { } } + +test Json.str get "\"\\\"\"" = { "string" = "\\\"" } + +test Json.str get "\"\\\"" = * + +test Json.str get "\"\"\"" = * + +test Json.str get "\"\\u1234\"" = { "string" = "\u1234" } + +(* Allow spurious backslashes; Issue #557 *) +test Json.str get "\"\\/\"" = { "string" = "\\/" } + +test lns get "{ \"download-dir\": \"\\/var\\/tmp\\/\" }" = + { "dict" + { "entry" = "download-dir" + { "string" = "\/var\/tmp\/" } } } diff --git a/lenses/tests/test_multipath.aug b/lenses/tests/test_multipath.aug index 02c95eb..6e877b0 100644 --- a/lenses/tests/test_multipath.aug +++ b/lenses/tests/test_multipath.aug @@ -192,4 +192,28 @@ test Multipath.lns get conf = { "path_grouping_policy" = "multibus" } { "polling_interval" = "9" } { "delay_watch_checks" = "10" } - { "delay_wait_checks" = "10" } } } \ No newline at end of file + { "delay_wait_checks" = "10" } } } + +test Multipath.lns get "blacklist { + devnode \".*\" + wwid \"([a-z]+|ab?c).*\" +}\n" = + { "blacklist" + { "devnode" = ".*" } + { "wwid" = "([a-z]+|ab?c).*" } } + +test Multipath.lns get "blacklist {\n property \"[a-z]+\"\n}\n" = + { "blacklist" + { "property" = "[a-z]+" } } + +(* Check that '""' inside a string works *) +test Multipath.lns get "blacklist { + device { + vendor SomeCorp + product \"2.5\"\" SSD\" + } +}\n" = + { "blacklist" + { "device" + { "vendor" = "SomeCorp" } + { "product" = "2.5\"\" SSD" } } } diff --git a/lenses/tests/test_nginx.aug b/lenses/tests/test_nginx.aug index f70ff52..d17b70a 100644 --- a/lenses/tests/test_nginx.aug +++ b/lenses/tests/test_nginx.aug @@ -275,3 +275,16 @@ test lns get "http { { "::1" = "2" } { "2001:0db8::" = "1" { "mask" = "32" } } } } + +test lns get "add_header X-XSS-Protection \"1; mode=block\" always;\n" = + { "add_header" = "X-XSS-Protection \"1; mode=block\" always" } + +test lns get "location /foo { + root /var/www/html; + internal; # only valid in location blocks +}\n" = + { "location" + { "#uri" = "/foo" } + { "root" = "/var/www/html" } + { "internal" + { "#comment" = "only valid in location blocks" } } } diff --git a/lenses/tests/test_redis.aug b/lenses/tests/test_redis.aug index 12cc25c..dbe854c 100644 --- a/lenses/tests/test_redis.aug +++ b/lenses/tests/test_redis.aug @@ -150,7 +150,7 @@ test Redis.lns get redis_conf = { "#comment" = "If you want you can bind a single interface, if the bind option is not" } { "#comment" = "specified all the interfaces will listen for incoming connections." } { } - { "bind" = "127.0.0.1" } + { "bind" { "ip" = "127.0.0.1" } } { } { "#comment" = "Note: you can disable saving at all commenting all the \"save\" lines." } { } @@ -179,3 +179,17 @@ test Redis.lns get redis_conf = Empty value (GH issue #115) *) test Redis.lns get "notify-keyspace-events \"\"\n" = { "notify-keyspace-events" = "" } + +(* Test: Redis.lns + Multiple bind IP addresses (GH issue #194) *) +test Redis.lns get "bind 127.0.0.1 \"::1\" 192.168.1.1\n" = + { "bind" + { "ip" = "127.0.0.1" } + { "ip" = "::1" } + { "ip" = "192.168.1.1" } } + +test Redis.lns get "bind 127.0.0.1\n bind 192.168.1.1\n" = + { "bind" + { "ip" = "127.0.0.1" } } + { "bind" + { "ip" = "192.168.1.1" } } diff --git a/lenses/tests/test_rsyslog.aug b/lenses/tests/test_rsyslog.aug index b71d32c..5386f83 100644 --- a/lenses/tests/test_rsyslog.aug +++ b/lenses/tests/test_rsyslog.aug @@ -199,3 +199,9 @@ test Rsyslog.lns get "module(load=\"imuxsock\" # provides support for local s { "SysSock.Use" = "off" } { "#comment" = "Turn off message reception via local log socket;" } } { "#comment" = "local messages are retrieved through imjournal now." } + +(* 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" } } diff --git a/lenses/tests/test_strongswan.aug b/lenses/tests/test_strongswan.aug new file mode 100644 index 0000000..55fe905 --- /dev/null +++ b/lenses/tests/test_strongswan.aug @@ -0,0 +1,65 @@ +(* + Some of the configuration snippets have been copied from the strongSwan + source tree. +*) + +module Test_Strongswan = + +(* conf/strongswan.conf *) +let default = " +# strongswan.conf - strongSwan configuration file +# +# Refer to the strongswan.conf(5) manpage for details +# +# Configuration changes should be made in the included files + +charon { + load_modular = yes + plugins { + include strongswan.d/charon/*.conf + } +} + +include strongswan.d/*.conf +" + +test Strongswan.lns get default = + { "#comment" = "strongswan.conf - strongSwan configuration file" } + { "#comment" = "Refer to the strongswan.conf(5) manpage for details" } + { "#comment" = "Configuration changes should be made in the included files" } + { "charon" + { "load_modular" = "yes" } + { "plugins" { "include" = "strongswan.d/charon/*.conf" } } + } + { "include" = "strongswan.d/*.conf" } + +(* conf/strongswan.conf.5.head.in *) +let man_example = " + a = b + section-one { + somevalue = asdf + subsection { + othervalue = xxx + } + # yei, a comment + yetanother = zz + } + section-two { + x = 12 + } +" + +test Strongswan.lns get man_example = + { "a" = "b" } + { "section-one" + { "somevalue" = "asdf" } + { "subsection" { "othervalue" = "xxx" } } + { "#comment" = "yei, a comment" } + { "yetanother" = "zz" } + } + { "section-two" { "x" = "12" } } + +test Strongswan.lns get "foo { bar = baz\n } quux {}\t#quuux\n" = + { "foo" { "bar" = "baz" } } + { "quux" } + { "#comment" = "quuux" } diff --git a/man/augmatch.pod b/man/augmatch.pod index 94ad944..076bd14 100644 --- a/man/augmatch.pod +++ b/man/augmatch.pod @@ -67,6 +67,10 @@ B will be searched for modules. Print only the value and not the label or the path of nodes. +=item B<-q>, B<--quiet> + +Do not print anything. Exit with zero status if a match was found + =back =head1 ENVIRONMENT VARIABLES @@ -98,6 +102,11 @@ F # show all the clients to which we are exporting /home augmatch -eom 'dir["/home"]/client' /etc/exports +=head1 EXIT STATUS + +The exit status is 0 when there was at least one match, 1 if there was no +match, and 2 if an error occurred. + =head1 FILES Lenses and schema definitions in F and diff --git a/src/augeas.c b/src/augeas.c index 8ec264c..df760f5 100644 --- a/src/augeas.c +++ b/src/augeas.c @@ -1958,7 +1958,7 @@ int aug_source(const augeas *aug, const char *path, char **file_path) { ERR_THROW(r == 0, aug, AUG_ENOMATCH, "There is no node matching %s", path); - tree_source(aug, match); + *file_path = tree_source(aug, match); ERR_BAIL(aug); result = 0; diff --git a/src/augeas.h b/src/augeas.h index d6a1a70..a877079 100644 --- a/src/augeas.h +++ b/src/augeas.h @@ -403,6 +403,8 @@ int aug_print(const augeas *aug, FILE *out, const char *path); * contain the path to the toplevel node of that file underneath /files. If * it does not, *FILE_PATH will be NULL. * + * The caller is responsible for freeing *FILE_PATH + * * Returns: * 0 on success, or a negative value on failure. It is an error if PATH * matches more than one node. diff --git a/src/augmatch.c b/src/augmatch.c index 20045c5..67c9d2a 100644 --- a/src/augmatch.c +++ b/src/augmatch.c @@ -25,11 +25,14 @@ #include #include #include +#include #include "memory.h" #include "augeas.h" #include +#define EXIT_TROUBLE 2 + #define cleanup(_x) __attribute__((__cleanup__(_x))) const char *progname; @@ -58,6 +61,8 @@ static void usage(void) { " -e, --exact print only exact matches instead of the entire tree\n" " starting at a match\n" " -o, --only-value print only the values of tree nodes, but no path\n" +" -q, --quiet do not print anything. Exit with zero status if a\n" +" match was found\n" " -r, --root ROOT use ROOT as the root of the filesystem\n" " -I, --include DIR search DIR for modules; can be given mutiple times\n" " -S, --nostdinc do not search the builtin default directories\n" @@ -82,7 +87,7 @@ static void die(bool cond, const char *format, ...) { va_start(args, format); vfprintf(stderr, format, args); va_end(args); - exit(EXIT_FAILURE); + exit(EXIT_TROUBLE); } } @@ -119,7 +124,7 @@ static void check_error(struct augeas *aug) { if (msg != NULL) { fprintf(stderr, "%s\n", msg); } - exit(EXIT_FAILURE); + exit(EXIT_TROUBLE); } } @@ -131,6 +136,7 @@ static void check_load_error(struct augeas *aug, const char *file) { const char *msg, *line, *col; aug_defvar(aug, "info", info); + free(info); die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file); aug_defvar(aug, "error", "$info/error"); @@ -153,7 +159,7 @@ static void check_load_error(struct augeas *aug, const char *file) { } else { fprintf(stderr, "error reading %s: %s\n", file, msg); } - exit(EXIT_FAILURE); + exit(EXIT_TROUBLE); } /* We keep track of where we are in the tree when we are printing it by @@ -236,17 +242,19 @@ static void print_tree(struct augeas *aug, int level, } /* Print the tree for file PATH (which must already start with /files), but - * only the nodes matching MATCH */ -static void print(struct augeas *aug, const char *path, const char *match) { + * only the nodes matching MATCH. + * + * Return EXIT_SUCCESS if there was at least one match, and EXIT_FAILURE + * if there was none. + */ +static int print(struct augeas *aug, const char *path, const char *match) { static const char *const match_var = "match"; - cleanup(freep) struct node *nodes = NULL; + struct node *nodes = NULL; nodes = calloc(max_nodes, sizeof(struct node)); oom_when(nodes == NULL); - aug_set(aug, "/augeas/context", path); - for (int i=0; i < max_nodes; i++) { nodes[i].var = format("var%d", i); } @@ -265,6 +273,12 @@ static void print(struct augeas *aug, const char *path, const char *match) { aug_defvar(aug, nodes[0].var, prefix); print_tree(aug, 0, prefix + strlen(path) + 1, nodes); } + for (int i=0; i < max_nodes; i++) { + free(nodes[i].var); + } + free(nodes); + + return (count == 0) ? EXIT_FAILURE : EXIT_SUCCESS; } /* Look at the filename and try to guess based on the extension. The @@ -297,6 +311,8 @@ int main(int argc, char **argv) { size_t matches_len = 0; const char *match = "*"; bool print_lens = false; + bool quiet = false; + int result = EXIT_SUCCESS; struct option options[] = { { "help", 0, 0, 'h' }, @@ -310,13 +326,14 @@ int main(int argc, char **argv) { { "root", 1, 0, 'r' }, { "print-lens", 0, 0, 'L' }, { "exact", 0, 0, 'e' }, + { "quiet", 0, 0, 'q' }, { 0, 0, 0, 0} }; unsigned int flags = AUG_NO_LOAD|AUG_NO_ERR_CLOSE; progname = basename(argv[0]); setlocale(LC_ALL, ""); - while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eL", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eLq", options, NULL)) != -1) { switch(opt) { case 'I': argz_add(&loadpath, &loadpath_len, optarg); @@ -353,10 +370,13 @@ int main(int argc, char **argv) { case 'e': print_exact = true; break; + case 'q': + quiet = true; + break; default: fprintf(stderr, "Try '%s --help' for more information.\n", progname); - exit(EXIT_FAILURE); + exit(EXIT_TROUBLE); break; } } @@ -365,7 +385,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Expected an input file\n"); fprintf(stderr, "Try '%s --help' for more information.\n", progname); - exit(EXIT_FAILURE); + exit(EXIT_TROUBLE); } const char *file = argv[optind]; @@ -414,13 +434,24 @@ int main(int argc, char **argv) { check_load_error(aug, file); char *path = format("/files%s", file); + aug_set(aug, "/augeas/context", path); + if (matches_len > 0) { argz_stringify(matches, matches_len, '|'); match = matches; } - print(aug, path, match); + if (quiet) { + int n = aug_match(aug, match, NULL); + check_error(aug); + result = (n == 0) ? EXIT_FAILURE : EXIT_SUCCESS; + } else { + result = print(aug, path, match); + } + free(path); + + return result; } /* diff --git a/src/augparse.c b/src/augparse.c index 96a686c..ad2342a 100644 --- a/src/augparse.c +++ b/src/augparse.c @@ -140,6 +140,7 @@ int main(int argc, char **argv) { if (s != NULL) { fprintf(stderr, "%s\n", s); } + aug_close(aug); exit(EXIT_FAILURE); } diff --git a/src/augtool.c b/src/augtool.c index ff097bd..b05fbfc 100644 --- a/src/augtool.c +++ b/src/augtool.c @@ -112,8 +112,7 @@ static char *readline_path_generator(const char *text, int state) { if ((path = strdup("*")) == NULL) return NULL; } else { - CALLOC(path, end - text + 2); - if (path == NULL) + if (ALLOC_N(path, end - text + 2) < 0) return NULL; strncpy(path, text, end - text); strcat(path, "*"); @@ -153,15 +152,13 @@ static char *readline_path_generator(const char *text, int state) { /* strip off context if the user didn't give it */ if (ctx != NULL) { - char *c = realloc(child, strlen(child)-strlen(ctx)+1); - if (c == NULL) { - free(child); - return NULL; - } int ctxidx = strlen(ctx); if (child[ctxidx] == SEP) ctxidx++; - strcpy(c, &child[ctxidx]); + char *c = strdup(&child[ctxidx]); + free(child); + if (c == NULL) + return NULL; child = c; } diff --git a/src/builtin.c b/src/builtin.c index 732ee10..7cf4fa0 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -42,8 +42,10 @@ */ /* V_REGEXP -> V_STRING -> V_LENS */ -static struct value *lns_del(struct info *info, - struct value *rxp, struct value *dflt) { +static struct value *lns_del(struct info *info, struct value **argv) { + struct value *rxp = argv[0]; + struct value *dflt = argv[1]; + assert(rxp->tag == V_REGEXP); assert(dflt->tag == V_STRING); return lns_make_prim(L_DEL, ref(info), @@ -51,44 +53,59 @@ static struct value *lns_del(struct info *info, } /* V_REGEXP -> V_LENS */ -static struct value *lns_store(struct info *info, struct value *rxp) { +static struct value *lns_store(struct info *info, struct value **argv) { + struct value *rxp = argv[0]; + assert(rxp->tag == V_REGEXP); return lns_make_prim(L_STORE, ref(info), ref(rxp->regexp), NULL); } /* V_STRING -> V_LENS */ -static struct value *lns_value(struct info *info, struct value *str) { +static struct value *lns_value(struct info *info, struct value **argv) { + struct value *str = argv[0]; + assert(str->tag == V_STRING); return lns_make_prim(L_VALUE, ref(info), NULL, ref(str->string)); } /* V_REGEXP -> V_LENS */ -static struct value *lns_key(struct info *info, struct value *rxp) { +static struct value *lns_key(struct info *info, struct value **argv) { + struct value *rxp = argv[0]; + assert(rxp->tag == V_REGEXP); return lns_make_prim(L_KEY, ref(info), ref(rxp->regexp), NULL); } /* V_STRING -> V_LENS */ -static struct value *lns_label(struct info *info, struct value *str) { +static struct value *lns_label(struct info *info, struct value **argv) { + struct value *str = argv[0]; + assert(str->tag == V_STRING); return lns_make_prim(L_LABEL, ref(info), NULL, ref(str->string)); } /* V_STRING -> V_LENS */ -static struct value *lns_seq(struct info *info, struct value *str) { +static struct value *lns_seq(struct info *info, struct value **argv) { + struct value *str = argv[0]; + assert(str->tag == V_STRING); return lns_make_prim(L_SEQ, ref(info), NULL, ref(str->string)); } /* V_STRING -> V_LENS */ -static struct value *lns_counter(struct info *info, struct value *str) { +static struct value *lns_counter(struct info *info, struct value **argv) { + struct value *str = argv[0]; + assert(str->tag == V_STRING); return lns_make_prim(L_COUNTER, ref(info), NULL, ref(str->string)); } /* V_LENS -> V_LENS -> V_LENS -> V_LENS */ -static struct value *lns_square(struct info *info, struct value *l1, - struct value *l2, struct value *l3) { +static struct value *lns_square(struct info *info, struct value **argv) { + struct value *l1 = argv[0]; + struct value *l2 = argv[1]; + struct value *l3 = argv[2]; + assert(l1->tag == V_LENS); assert(l2->tag == V_LENS); assert(l3->tag == V_LENS); @@ -179,8 +196,10 @@ static struct value *pathx_parse_glue(struct info *info, struct value *tree, } /* V_LENS -> V_STRING -> V_TREE */ -static struct value *lens_get(struct info *info, struct value *l, - struct value *str) { +static struct value *lens_get(struct info *info, struct value **argv) { + struct value *l = argv[0]; + struct value *str = argv[1]; + assert(l->tag == V_LENS); assert(str->tag == V_STRING); struct lns_error *err; @@ -210,8 +229,11 @@ static struct value *lens_get(struct info *info, struct value *l, /* V_LENS -> V_TREE -> V_STRING -> V_STRING */ -static struct value *lens_put(struct info *info, struct value *l, - struct value *tree, struct value *str) { +static struct value *lens_put(struct info *info, struct value **argv) { + struct value *l = argv[0]; + struct value *tree = argv[1]; + struct value *str = argv[2]; + assert(l->tag == V_LENS); assert(tree->tag == V_TREE); assert(str->tag == V_STRING); @@ -237,11 +259,14 @@ static struct value *lens_put(struct info *info, struct value *l, } /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ -static struct value *tree_set_glue(struct info *info, struct value *path, - struct value *val, struct value *tree) { +static struct value *tree_set_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first + struct value *path = argv[0]; + struct value *val = argv[1]; + struct value *tree = argv[2]; + assert(path->tag == V_STRING); assert(val->tag == V_STRING); assert(tree->tag == V_TREE); @@ -277,11 +302,13 @@ static struct value *tree_set_glue(struct info *info, struct value *path, } /* V_STRING -> V_TREE -> V_TREE */ -static struct value *tree_clear_glue(struct info *info, struct value *path, - struct value *tree) { +static struct value *tree_clear_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first + struct value *path = argv[0]; + struct value *tree = argv[1]; + assert(path->tag == V_STRING); assert(tree->tag == V_TREE); @@ -349,25 +376,32 @@ static struct value *tree_insert_glue(struct info *info, struct value *label, /* Insert after */ /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ -static struct value *tree_insa_glue(struct info *info, struct value *label, - struct value *path, struct value *tree) { +static struct value *tree_insa_glue(struct info *info, struct value **argv) { + struct value *label = argv[0]; + struct value *path = argv[1]; + struct value *tree = argv[2]; + return tree_insert_glue(info, label, path, tree, 0); } /* Insert before */ /* V_STRING -> V_STRING -> V_TREE -> V_TREE */ -static struct value *tree_insb_glue(struct info *info, struct value *label, - struct value *path, struct value *tree) { +static struct value *tree_insb_glue(struct info *info, struct value **argv) { + struct value *label = argv[0]; + struct value *path = argv[1]; + struct value *tree = argv[2]; + return tree_insert_glue(info, label, path, tree, 1); } /* V_STRING -> V_TREE -> V_TREE */ -static struct value *tree_rm_glue(struct info *info, - struct value *path, - struct value *tree) { +static struct value *tree_rm_glue(struct info *info, struct value **argv) { // FIXME: This only works if TREE is not referenced more than once; // otherwise we'll have some pretty weird semantics, and would really // need to copy TREE first + struct value *path = argv[0]; + struct value *tree = argv[1]; + assert(path->tag == V_STRING); assert(tree->tag == V_TREE); @@ -390,7 +424,9 @@ static struct value *tree_rm_glue(struct info *info, } /* V_STRING -> V_STRING */ -static struct value *gensym(struct info *info, struct value *prefix) { +static struct value *gensym(struct info *info, struct value **argv) { + struct value *prefix = argv[0]; + assert(prefix->tag == V_STRING); static unsigned int count = 0; struct value *v; @@ -406,7 +442,9 @@ static struct value *gensym(struct info *info, struct value *prefix) { } /* V_STRING -> V_FILTER */ -static struct value *xform_incl(struct info *info, struct value *s) { +static struct value *xform_incl(struct info *info, struct value **argv) { + struct value *s = argv[0]; + assert(s->tag == V_STRING); struct value *v = make_value(V_FILTER, ref(info)); v->filter = make_filter(ref(s->string), 1); @@ -414,7 +452,9 @@ static struct value *xform_incl(struct info *info, struct value *s) { } /* V_STRING -> V_FILTER */ -static struct value *xform_excl(struct info *info, struct value *s) { +static struct value *xform_excl(struct info *info, struct value **argv) { + struct value *s = argv[0]; + assert(s->tag == V_STRING); struct value *v = make_value(V_FILTER, ref(info)); v->filter = make_filter(ref(s->string), 0); @@ -422,8 +462,10 @@ static struct value *xform_excl(struct info *info, struct value *s) { } /* V_LENS -> V_FILTER -> V_TRANSFORM */ -static struct value *xform_transform(struct info *info, struct value *l, - struct value *f) { +static struct value *xform_transform(struct info *info, struct value **argv) { + struct value *l = argv[0]; + struct value *f = argv[1]; + assert(l->tag == V_LENS); assert(f->tag == V_FILTER); if (l->lens->value || l->lens->key) { @@ -436,14 +478,16 @@ static struct value *xform_transform(struct info *info, struct value *l, return v; } -static struct value *sys_getenv(struct info *info, struct value *n) { - assert(n->tag == V_STRING); +static struct value *sys_getenv(struct info *info, struct value **argv) { + assert(argv[0]->tag == V_STRING); struct value *v = make_value(V_STRING, ref(info)); - v->string = dup_string(getenv(n->string->str)); + v->string = dup_string(getenv(argv[0]->string->str)); return v; } -static struct value *sys_read_file(struct info *info, struct value *n) { +static struct value *sys_read_file(struct info *info, struct value **argv) { + struct value *n = argv[0]; + assert(n->tag == V_STRING); char *str = NULL; @@ -464,7 +508,10 @@ static struct value *sys_read_file(struct info *info, struct value *n) { /* V_LENS -> V_LENS */ static struct value *lns_check_rec_glue(struct info *info, - struct value *l, struct value *r) { + struct value **argv) { + struct value *l = argv[0]; + struct value *r = argv[1]; + assert(l->tag == V_LENS); assert(r->tag == V_LENS); int check = typecheck_p(info); @@ -477,28 +524,28 @@ static struct value *lns_check_rec_glue(struct info *info, */ /* V_STRING -> V_UNIT */ -static struct value *pr_string(struct info *info, struct value *s) { - printf("%s", s->string->str); +static struct value *pr_string(struct info *info, struct value **argv) { + printf("%s", argv[0]->string->str); return make_unit(ref(info)); } /* V_REGEXP -> V_UNIT */ -static struct value *pr_regexp(struct info *info, struct value *r) { - print_regexp(stdout, r->regexp); +static struct value *pr_regexp(struct info *info, struct value **argv) { + print_regexp(stdout, argv[0]->regexp); return make_unit(ref(info)); } /* V_STRING -> V_UNIT */ -static struct value *pr_endline(struct info *info, struct value *s) { - printf("%s\n", s->string->str); +static struct value *pr_endline(struct info *info, struct value **argv) { + printf("%s\n", argv[0]->string->str); return make_unit(ref(info)); } /* V_TREE -> V_TREE */ static struct value *pr_tree(ATTRIBUTE_UNUSED struct info *info, - struct value *t) { - print_tree_braces(stdout, 0, t->origin); - return ref(t); + struct value **argv) { + print_tree_braces(stdout, 0, argv[0]->origin); + return ref(argv[0]); } /* @@ -515,27 +562,29 @@ static struct value *lns_value_of_type(struct info *info, struct regexp *rx) { } /* V_LENS -> V_REGEXP */ -static struct value *lns_ctype(struct info *info, struct value *l) { - return lns_value_of_type(info, l->lens->ctype); +static struct value *lns_ctype(struct info *info, struct value **argv) { + return lns_value_of_type(info, argv[0]->lens->ctype); } /* V_LENS -> V_REGEXP */ -static struct value *lns_atype(struct info *info, struct value *l) { - return lns_value_of_type(info, l->lens->atype); +static struct value *lns_atype(struct info *info, struct value **argv) { + return lns_value_of_type(info, argv[0]->lens->atype); } /* V_LENS -> V_REGEXP */ -static struct value *lns_vtype(struct info *info, struct value *l) { - return lns_value_of_type(info, l->lens->vtype); +static struct value *lns_vtype(struct info *info, struct value **argv) { + return lns_value_of_type(info, argv[0]->lens->vtype); } /* V_LENS -> V_REGEXP */ -static struct value *lns_ktype(struct info *info, struct value *l) { - return lns_value_of_type(info, l->lens->ktype); +static struct value *lns_ktype(struct info *info, struct value **argv) { + return lns_value_of_type(info, argv[0]->lens->ktype); } /* V_LENS -> V_STRING */ -static struct value *lns_fmt_atype(struct info *info, struct value *l) { +static struct value *lns_fmt_atype(struct info *info, struct value **argv) { + struct value *l = argv[0]; + struct value *result = NULL; char *s = NULL; int r; @@ -549,8 +598,10 @@ static struct value *lns_fmt_atype(struct info *info, struct value *l) { } /* V_REGEXP -> V_STRING -> V_STRING */ -static struct value *rx_match(struct info *info, - struct value *rx, struct value *s) { +static struct value *rx_match(struct info *info, struct value **argv) { + struct value *rx = argv[0]; + struct value *s = argv[1]; + struct value *result = NULL; const char *str = s->string->str; struct re_registers regs; diff --git a/src/fa.c b/src/fa.c index 412d139..8e1d7d4 100644 --- a/src/fa.c +++ b/src/fa.c @@ -286,6 +286,11 @@ static void fa_dot_debug(struct fa *fa, const char *tag) { return; fp = fopen(fname, "w"); + if (fp == NULL) { + free(fname); + return; + } + fa_dot(fp, fa); fclose(fp); free(fname); diff --git a/src/get.c b/src/get.c index ce0864b..94b9ba2 100644 --- a/src/get.c +++ b/src/get.c @@ -288,7 +288,8 @@ static void get_error(struct state *state, struct lens *lens, static struct skel *make_skel(struct lens *lens) { struct skel *skel; enum lens_tag tag = lens->tag; - CALLOC(skel, 1); + if (ALLOC(skel) < 0) + return NULL; skel->tag = tag; return skel; } @@ -487,7 +488,8 @@ static struct seq *find_seq(const char *name, struct state *state) { seq = seq->next); if (seq == NULL) { - CALLOC(seq, 1); + if (ALLOC(seq) < 0) + return NULL; seq->name = name; seq->value = 1; list_append(state->seqs, seq); diff --git a/src/internal.h b/src/internal.h index 2633a98..482648a 100644 --- a/src/internal.h +++ b/src/internal.h @@ -268,13 +268,8 @@ static inline int pathendswith(const char *path, const char *basenam) { Allocate as needed. Return 0 on success, -1 on failure */ int pathjoin(char **path, int nseg, ...); -/* Call calloc to allocate an array of N instances of *VAR */ -#define CALLOC(Var,N) do { (Var) = calloc ((N), sizeof (*(Var))); } while (0) - #define MEMZERO(ptr, n) memset((ptr), 0, (n) * sizeof(*(ptr))); -#define MEMCPY(dest, src, n) memcpy((dest), (src), (n) * sizeof(*(src))) - #define MEMMOVE(dest, src, n) memmove((dest), (src), (n) * sizeof(*(src))) /** diff --git a/src/jmt.c b/src/jmt.c index 79953da..86179ee 100644 --- a/src/jmt.c +++ b/src/jmt.c @@ -1109,16 +1109,20 @@ static ind_t add_lens(struct jmt *jmt, struct lens *lens) { if (debugging("cf.jmt")) { if (sA == NULL) { + char *s = format_lens(lens); printf("add_lens: "); print_regexp(stdout, lens->ctype); - printf(" %s\n", format_lens(lens)); + printf(" %s\n", s); + free(s); } else { + char *s = format_lens(lens); printf("add_lens: "); flens(stdout, l); - printf(" %u %s\n", sA->num, format_lens(lens)); + printf(" %u %s\n", sA->num, s); if (nullable) { - printf("add_lens: // %s\n", format_lens(lens)); + printf("add_lens: // %s\n", s); } + free(s); } } diff --git a/src/lens.c b/src/lens.c index 220668c..7234b8f 100644 --- a/src/lens.c +++ b/src/lens.c @@ -583,7 +583,7 @@ typecheck_prim(enum lens_tag tag, struct info *info, fa_isect = fa_intersect(fa_slash, fa_key); if (! fa_is_basic(fa_isect, FA_EMPTY)) { exn = make_exn_value(info, - "The key regexp /%s/ matches a '/'", regexp->pattern->str); + "The key regexp /%s/ matches a '/' which is used to separate nodes.", regexp->pattern->str); goto error; } fa_free(fa_isect); @@ -670,6 +670,8 @@ struct value *lns_make_prim(enum lens_tag tag, struct info *info, lens->vtype = restrict_regexp(lens->regexp); } else if (tag == L_VALUE) { lens->vtype = make_regexp_literal(info, lens->string->str); + if (lens->vtype == NULL) + goto error; } return make_lens_value(lens); diff --git a/src/pathx.c b/src/pathx.c index 48c8b0b..35be7a2 100644 --- a/src/pathx.c +++ b/src/pathx.c @@ -208,6 +208,10 @@ struct expr { struct { /* E_APP */ const struct func *func; struct expr **args; + /* If fold is true, replace this function invocation + * with its value after the first time we evaluate this + * expression */ + bool fold; }; }; }; @@ -282,6 +286,7 @@ struct func { const char *name; unsigned int arity; enum type type; + bool pure; /* Result only depends on args */ const enum type *arg_types; func_impl_t impl; }; @@ -304,40 +309,40 @@ static const enum type arg_types_nodeset_string[] = { T_NODESET, T_STRING }; static const struct func builtin_funcs[] = { { .name = "last", .arity = 0, .type = T_NUMBER, .arg_types = NULL, - .impl = func_last }, + .impl = func_last, .pure = false }, { .name = "position", .arity = 0, .type = T_NUMBER, .arg_types = NULL, - .impl = func_position }, + .impl = func_position, .pure = false }, { .name = "label", .arity = 0, .type = T_STRING, .arg_types = NULL, - .impl = func_label }, + .impl = func_label, .pure = false }, { .name = "count", .arity = 1, .type = T_NUMBER, .arg_types = arg_types_nodeset, - .impl = func_count }, + .impl = func_count, .pure = false }, { .name = "regexp", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_string, - .impl = func_regexp }, + .impl = func_regexp, .pure = true }, { .name = "regexp", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_nodeset, - .impl = func_regexp }, + .impl = func_regexp, .pure = true }, { .name = "regexp", .arity = 2, .type = T_REGEXP, .arg_types = arg_types_string_string, - .impl = func_regexp_flag }, + .impl = func_regexp_flag, .pure = true }, { .name = "regexp", .arity = 2, .type = T_REGEXP, .arg_types = arg_types_nodeset_string, - .impl = func_regexp_flag }, + .impl = func_regexp_flag, .pure = true }, { .name = "glob", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_string, - .impl = func_glob }, + .impl = func_glob, .pure = true }, { .name = "glob", .arity = 1, .type = T_REGEXP, .arg_types = arg_types_nodeset, - .impl = func_glob }, + .impl = func_glob, .pure = true }, { .name = "int", .arity = 1, .type = T_NUMBER, - .arg_types = arg_types_string, .impl = func_int }, + .arg_types = arg_types_string, .impl = func_int, .pure = false }, { .name = "int", .arity = 1, .type = T_NUMBER, - .arg_types = arg_types_nodeset, .impl = func_int }, + .arg_types = arg_types_nodeset, .impl = func_int, .pure = false }, { .name = "int", .arity = 1, .type = T_NUMBER, - .arg_types = arg_types_bool, .impl = func_int }, + .arg_types = arg_types_bool, .impl = func_int, .pure = false }, { .name = "not", .arity = 1, .type = T_BOOLEAN, - .arg_types = arg_types_bool, .impl = func_not } + .arg_types = arg_types_bool, .impl = func_not, .pure = true } }; #define RET_ON_ERROR \ @@ -1409,6 +1414,16 @@ static void eval_expr(struct expr *expr, struct state *state) { break; case E_APP: eval_app(expr, state); + if (expr->fold) { + /* Do constant folding: replace the function application with + * a reference to the value that resulted from evaluating it */ + for (int i=0; i < expr->func->arity; i++) + free_expr(expr->args[i]); + free(expr->args); + value_ind_t vind = state->values_used - 1; + expr->tag = E_VALUE; + expr->value_ind = state->values[vind]; + } break; default: assert(0); @@ -1493,6 +1508,18 @@ static void check_app(struct expr *expr, struct state *state) { if (f < ARRAY_CARDINALITY(builtin_funcs)) { expr->func = builtin_funcs + f; expr->type = expr->func->type; + expr->fold = expr->func->pure; + if (expr->fold) { + /* We only do constant folding for invocations of pure functions + * whose arguments are literal values. That misses opportunities + * for constant folding, e.g., "regexp('foo' + 'bar')" but is + * a bit simpler than doing full tracking of constants + */ + for (int i=0; i < expr->func->arity; i++) { + if (expr->args[i]->tag != E_VALUE) + expr->fold = false; + } + } } else { STATE_ERROR(state, PATHX_ETYPE); } @@ -1971,7 +1998,7 @@ static void parse_location_path(struct state *state) { state->pos += 1; locpath = parse_relative_location_path(state); if (HAS_ERROR(state)) - return; + goto error; struct step *step = make_step(DESCENDANT_OR_SELF, state); if (HAS_ERROR(state)) goto error; diff --git a/src/put.c b/src/put.c index 5ada2ce..bcc3de2 100644 --- a/src/put.c +++ b/src/put.c @@ -80,7 +80,8 @@ static void put_error(struct state *state, struct lens *lens, if (state->error != NULL) return; - CALLOC(state->error, 1); + if (ALLOC(state->error) < 0) + return; state->error->lens = ref(lens); state->error->pos = -1; if (strlen(state->path) == 0) { @@ -174,7 +175,8 @@ static struct split *split_append(struct split **split, struct split *tail, struct tree *tree, struct tree *follow, char *enc, size_t start, size_t end) { struct split *sp; - CALLOC(sp, 1); + if (ALLOC(sp) < 0) + return NULL; sp->tree = tree; sp->follow = follow; sp->enc = enc; @@ -217,6 +219,8 @@ static struct split *split_concat(struct state *state, struct lens *lens) { for (int i=0; i < lens->nchildren; i++) { tail = split_append(&split, tail, NULL, NULL, outer->enc, 0, 0); + if (tail == NULL) + goto error; } return split; } diff --git a/src/regexp.c b/src/regexp.c index d460361..ad5ae65 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -234,7 +234,8 @@ struct regexp *make_regexp_literal(struct info *info, const char *text) { /* Escape special characters in text since it should be taken literally */ - CALLOC(pattern, 2*strlen(text)+1); + if (ALLOC_N(pattern, 2*strlen(text)+1) < 0) + return NULL; p = pattern; for (const char *t = text; *t != '\0'; t++) { if ((*t == '\\') && t[1]) { @@ -544,8 +545,10 @@ static int regexp_compile_internal(struct regexp *r, const char **c) { *c = NULL; - if (r->re == NULL) - CALLOC(r->re, 1); + if (r->re == NULL) { + if (ALLOC(r->re) < 0) + return -1; + } re_syntax_options = syntax; if (r->nocase) diff --git a/src/syntax.c b/src/syntax.c index 612544c..894c24a 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -414,7 +414,8 @@ struct value *make_exn_value(struct info *info, return NULL; v = make_value(V_EXN, ref(info)); - CALLOC(v->exn, 1); + if (ALLOC(v->exn) < 0) + return info->error->exn; v->exn->info = info; v->exn->message = message; @@ -954,6 +955,9 @@ static struct value *coerce(struct value *v, struct type *t) { if (vt->tag == T_STRING && t->tag == T_REGEXP) { struct value *rxp = make_value(V_REGEXP, ref(v->info)); rxp->regexp = make_regexp_literal(v->info, v->string->str); + if (rxp->regexp == NULL) { + report_error(v->info->error, AUG_ENOMEM, NULL); + }; unref(v, value); unref(vt, type); return rxp; @@ -982,7 +986,8 @@ static struct type *expect_types_arr(struct info *info, } len += (ntypes - 1) * 4 + 1; char *allowed_names; - CALLOC(allowed_names, len); + if (ALLOC_N(allowed_names, len) < 0) + return NULL; for (int i=0; i < ntypes; i++) { if (i > 0) strcat(allowed_names, (i == ntypes - 1) ? ", or " : ", "); @@ -1023,42 +1028,16 @@ typedef struct value *(*impl5)(struct info *, struct value *, struct value *, static struct value *native_call(struct info *info, struct native *func, struct ctx *ctx) { - struct value *argv[func->argc]; + struct value *argv[func->argc + 1]; struct binding *b = ctx->local; - struct value *result; for (int i = func->argc - 1; i >= 0; i--) { argv[i] = b->value; b = b->next; } + argv[func->argc] = NULL; - switch(func->argc) { - case 0: - result = ((impl0) *func->impl)(info); - break; - case 1: - result = ((impl1) *func->impl)(info, argv[0]); - break; - case 2: - result = ((impl2) *func->impl)(info, argv[0], argv[1]); - break; - case 3: - result = ((impl3) *func->impl)(info, argv[0], argv[1], argv[2]); - break; - case 4: - result = ((impl4) *func->impl)(info, argv[0], argv[1], argv[2], argv[3]); - break; - case 5: - result = ((impl5) *func->impl)(info, argv[0], argv[1], argv[2], argv[3], - argv[4]); - break; - default: - assert(0); - abort(); - break; - } - - return result; + return func->impl(info, argv); } static void type_error1(struct info *info, const char *msg, struct type *type) { @@ -1535,7 +1514,8 @@ static struct value *compile_concat(struct term *exp, struct ctx *ctx) { const char *s2 = v2->string->str; v = make_value(V_STRING, ref(info)); make_ref(v->string); - CALLOC(v->string->str, strlen(s1) + strlen(s2) + 1); + if (ALLOC_N(v->string->str, strlen(s1) + strlen(s2) + 1) < 0) + goto error; char *s = v->string->str; strcpy(s, s1); strcat(s, s2); @@ -1574,6 +1554,8 @@ static struct value *compile_concat(struct term *exp, struct ctx *ctx) { unref(v1, value); unref(v2, value); return v; + error: + return exp->info->error->exn; } static struct value *apply(struct term *app, struct ctx *ctx) { @@ -1857,7 +1839,7 @@ make_native_info(struct error *error, const char *fname, int line) { int define_native_intl(const char *file, int line, struct error *error, struct module *module, const char *name, - int argc, void *impl, ...) { + int argc, func_impl impl, ...) { assert(argc > 0); /* We have no unit type */ assert(argc <= 5); va_list ap; @@ -2086,6 +2068,7 @@ int interpreter_init(struct augeas *aug) { for (int i=0; i < globbuf.gl_pathc; i++) { char *name, *p, *q; + int res; p = strrchr(globbuf.gl_pathv[i], SEP); if (p == NULL) p = globbuf.gl_pathv[i]; @@ -2094,9 +2077,10 @@ int interpreter_init(struct augeas *aug) { q = strchr(p, '.'); name = strndup(p, q - p); name[0] = toupper(name[0]); - if (load_module(aug, name) == -1) - goto error; + res = load_module(aug, name); free(name); + if (res == -1) + goto error; } globfree(&globbuf); return 0; diff --git a/src/syntax.h b/src/syntax.h index 12c3bae..30aefe5 100644 --- a/src/syntax.h +++ b/src/syntax.h @@ -112,10 +112,17 @@ struct param { struct type *type; }; +/* The protoype for the implementation of a native/builtin function in the + * interpreter. + * + * The arguments are passed as a NULL-terminated array of values. + */ +typedef struct value *(*func_impl)(struct info *, struct value *argv[]); + struct native { unsigned int argc; struct type *type; - struct value *(*impl)(void); + func_impl impl; }; /* An exception in the interpreter. Some exceptions are reported directly @@ -270,7 +277,7 @@ ATTRIBUTE_RETURN_CHECK int define_native_intl(const char *fname, int line, struct error *error, struct module *module, const char *name, - int argc, void *impl, ...); + int argc, func_impl impl, ...); struct module *builtin_init(struct error *); diff --git a/src/transform.c b/src/transform.c index 6a0ce85..176482b 100644 --- a/src/transform.c +++ b/src/transform.c @@ -1258,7 +1258,7 @@ int transform_save(struct augeas *aug, struct tree *xfm, if (fchmod(fileno(fp), 0666 & ~curumsk) < 0) { err_status = "create_chmod"; - return -1; + goto done; } } diff --git a/tests/Makefile.am b/tests/Makefile.am index 8e035e9..fb20ffe 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,7 +6,7 @@ AM_CFLAGS = $(AUGEAS_CFLAGS) $(WARN_CFLAGS) $(GNULIB_CFLAGS) \ VALGRIND=libtool --mode=execute valgrind --quiet --leak-check=full valgrind: - make check \ + $(MAKE) $(MAKEFLAGS) check \ VALGRIND="$(VALGRIND)" \ AUGPARSE=$(abs_top_builddir)/src/augparse \ AUGTOOL=$(abs_top_builddir)/src/augtool @@ -205,6 +205,7 @@ lens_tests = \ lens-sshd.sh \ lens-sssd.sh \ lens-star.sh \ + lens-strongswan.sh \ lens-stunnel.sh \ lens-subversion.sh \ lens-sysconfig.sh \ @@ -272,7 +273,7 @@ check_SCRIPTS = \ test-save-empty.sh test-bug-1.sh test-idempotent.sh test-preserve.sh \ test-events-saved.sh test-save-mode.sh test-unlink-error.sh \ test-augtool-empty-line.sh test-augtool-modify-root.sh \ - test-span-rec-lens.sh test-nonwritable.sh + test-span-rec-lens.sh test-nonwritable.sh test-augmatch.sh EXTRA_DIST = \ test-augtool root lens-test-1 \ diff --git a/tests/test-api.c b/tests/test-api.c index fda8ab6..6460d7b 100644 --- a/tests/test-api.c +++ b/tests/test-api.c @@ -816,6 +816,41 @@ static void testAugNs(CuTest *tc) { aug_close(aug); } +/* Test aug_source */ +static void testAugSource(CuTest *tc) { + struct augeas *aug; + int r; + char *s; + + aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD); + CuAssertPtrNotNull(tc, aug); + CuAssertIntEquals(tc, AUG_NOERROR, aug_error(aug)); + + r = aug_load_file(aug, "/etc/hosts"); + CuAssertIntEquals(tc, 0, r); + + r = aug_source(aug, "/files/etc/hosts/1", &s); + CuAssertIntEquals(tc, 0, r); + CuAssertStrEquals(tc, "/files/etc/hosts", s); + free(s); + + r = aug_source(aug, "/files/etc/fstab", &s); + CuAssertIntEquals(tc, -1, r); + CuAssertIntEquals(tc, AUG_ENOMATCH, aug_error(aug)); + CuAssertPtrEquals(tc, NULL, s); + + r = aug_source(aug, "/files[", &s); + CuAssertIntEquals(tc, -1, r); + CuAssertIntEquals(tc, AUG_EPATHX, aug_error(aug)); + CuAssertPtrEquals(tc, NULL, s); + + r = aug_source(aug, "/files/etc/hosts/*", &s); + CuAssertIntEquals(tc, -1, r); + CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug)); + CuAssertPtrEquals(tc, NULL, s); + +} + int main(void) { char *output = NULL; CuSuite* suite = CuSuiteNew(); @@ -840,6 +875,7 @@ int main(void) { SUITE_ADD_TEST(suite, testLoadBadPath); SUITE_ADD_TEST(suite, testLoadBadLens); SUITE_ADD_TEST(suite, testAugNs); + SUITE_ADD_TEST(suite, testAugSource); abs_top_srcdir = getenv("abs_top_srcdir"); if (abs_top_srcdir == NULL) diff --git a/tests/test-augmatch.sh b/tests/test-augmatch.sh new file mode 100755 index 0000000..9ef4218 --- /dev/null +++ b/tests/test-augmatch.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# Tests for augmatch + +TOPDIR=$(cd $(dirname $0)/.. && pwd) +[ -n "$abs_top_srcdir" ] || abs_top_srcdir=$TOPDIR + +export AUGEAS_LENS_LIB=$abs_top_srcdir/lenses +export AUGEAS_ROOT=$abs_top_srcdir/tests/root + +fail() { + echo "failed: $*" + exit 1 +} + +assert_eq() { + if [ "$1" != "$2" ]; then + shift 2 + fail $* + fi +} + +# print the tree for /etc/exports +act=$(augmatch /etc/exports) +assert_eq 23 $(echo "$act" | wc -l) "t1: expected 23 lines of output" + +# show only the entry for a specific mount +act=$(augmatch -m 'dir["/home"]' /etc/exports) +assert_eq 9 $(echo "$act" | wc -l) "t2: expected 9 lines of output" + +# show all the clients to which we are exporting /home +act=$(augmatch -eom 'dir["/home"]/client' /etc/exports) +exp=$(printf "207.46.0.0/16\n192.168.50.2/32\n") +assert_eq "$exp" "$act" "t3: expected '$exp'" + +# report errors with exit code 2 +augmatch -m '**' /etc/exports >/dev/null 2>&1 +ret=$? +assert_eq 2 $ret "t4: expected exit code 2 but got $ret" + +augmatch /etc >/dev/null 2>&1 +ret=$? +assert_eq 2 $ret "t5: expected exit code 2 but got $ret" + +# test --quiet +act=$(augmatch -q -m "dir['/local']" /etc/exports) +ret=$? +assert_eq '' "$act" "t6: expected no output" +assert_eq 0 $ret "t6: expected exit code 0 but got $ret" + +act=$(augmatch -q -m "dir['/not_there']" /etc/exports) +ret=$? +assert_eq '' "$act" "t7: expected no output" +assert_eq 1 $ret "t7: expected exit code 1 but got $ret" -- 2.7.4