From ab13fbe95e2c6ee679820bf205352ce44d25efda Mon Sep 17 00:00:00 2001 From: Tom Eastep Date: Fri, 16 Mar 2012 08:16:12 -0700 Subject: [PATCH] Allow conditional compilation Signed-off-by: Tom Eastep --- Shorewall/Perl/Shorewall/Config.pm | 96 +++++++++++--- Shorewall/manpages/shorewall-params.xml | 21 +-- Shorewall6/manpages/shorewall6-params.xml | 10 +- docs/configuration_file_basics.xml | 151 +++++++++++++++++++--- 4 files changed, 236 insertions(+), 42 deletions(-) diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm index 6571397ab..261b66d8a 100644 --- a/Shorewall/Perl/Shorewall/Config.pm +++ b/Shorewall/Perl/Shorewall/Config.pm @@ -425,6 +425,13 @@ my %deprecated = ( LOGRATE => '' , # my %converted = ( WIDE_TC_MARKS => 1, HIGH_ROUTE_MARKS => 1 ); +# +# Variables involved in ?IF, ?ELSE ?ENDIF processing +# +my $omitting; +my @ifstack; +my $ifstack; + # # Rather than initializing globals in an INIT block or during declaration, # we initialize them in a function. This is done for two reasons: @@ -458,6 +465,9 @@ sub initialize( $ ) { $tempfile = ''; # Temporary File Name $sillyname = $sillyname1 = ''; # Temporary ipchains + $omitting = 0; + $ifstack = 0; + @ifstack = (); # # Misc Globals @@ -756,7 +766,7 @@ my @abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); sub warning_message { my $linenumber = $currentlinenumber || 1; - my $currentlineinfo = $currentfile ? " : $currentfilename (line $linenumber)" : ''; + my $currentlineinfo = $currentfile ? " : $currentfilename " . ( $linenumber eq 'EOF' ? '(EOF)' : "(line $linenumber)" ) : ''; our @localtime; $| = 1; #Reset output buffering (flush any partially filled buffers). @@ -812,7 +822,7 @@ sub cleanup() { # sub fatal_error { my $linenumber = $currentlinenumber || 1; - my $currentlineinfo = $currentfile ? " : $currentfilename (line $linenumber)" : ''; + my $currentlineinfo = $currentfile ? " : $currentfilename " . ( $linenumber eq 'EOF' ? '(EOF)' : "(line $linenumber)" ) : ''; $| = 1; #Reset output buffering (flush any partially filled buffers). @@ -1481,10 +1491,17 @@ sub open_file( $ ) { sub pop_include() { my $arrayref = pop @includestack; + unless ( $ifstack == @ifstack ) { + my $lastref = $ifstack[-1]; + $currentlinenumber = 'EOF'; + fatal_error qq(Missing "?END" to match ?IF at line number $lastref->[2]) + } + if ( $arrayref ) { - ( $currentfile, $currentfilename, $currentlinenumber ) = @$arrayref; + ( $currentfile, $currentfilename, $currentlinenumber, $ifstack ) = @$arrayref; } else { - $currentfile = undef; + $currentfile = undef; + $currentlinenumber = 'EOF'; } } @@ -1607,7 +1624,7 @@ sub copy1( $ ) { fatal_error "Directory ($filename) not allowed in INCLUDE" if -d _; if ( -s _ ) { - push @includestack, [ $currentfile, $currentfilename, $currentlinenumber ]; + push @includestack, [ $currentfile, $currentfilename, $currentlinenumber, $ifstack ]; $currentfile = undef; do_open_file $filename; } else { @@ -1723,7 +1740,7 @@ EOF # sub push_open( $ ) { - push @includestack, [ $currentfile, $currentfilename, $currentlinenumber ]; + push @includestack, [ $currentfile, $currentfilename, $currentlinenumber, $ifstack = @ifstack ]; my @a = @includestack; push @openstack, \@a; @includestack = (); @@ -1798,7 +1815,7 @@ sub embedded_shell( $ ) { while ( <$currentfile> ) { $currentlinenumber++; - last if $last = s/^\s*END(\s+SHELL)?\s*;?//; + last if $last = s/^\s*\??END(\s+SHELL)?\s*;?//; $command .= $_; } @@ -1808,7 +1825,7 @@ sub embedded_shell( $ ) { $command .= q('); - push @includestack, [ $currentfile, $currentfilename, $currentlinenumber ]; + push @includestack, [ $currentfile, $currentfilename, $currentlinenumber, $ifstack = @ifstack ]; $currentfile = undef; open $currentfile , '-|', $command or fatal_error qq(Shell Command failed); $currentfilename = "SHELL\@$currentfilename:$currentlinenumber"; @@ -1832,7 +1849,7 @@ sub embedded_perl( $ ) { while ( <$currentfile> ) { $currentlinenumber++; - last if $last = s/^\s*END(\s+PERL)?\s*;?//; + last if $last = s/^\s*\??END(\s+PERL)?\s*;?//; $command .= $_; } @@ -1864,7 +1881,7 @@ sub embedded_perl( $ ) { $perlscript = undef; - push @includestack, [ $currentfile, $currentfilename, $currentlinenumber ]; + push @includestack, [ $currentfile, $currentfilename, $currentlinenumber , $ifstack = @ifstack ]; $currentfile = undef; open $currentfile, '<', $perlscriptname or fatal_error "Unable to open Perl Script $perlscriptname"; @@ -1980,6 +1997,7 @@ sub expand_variables( \$ ) { # - Handle embedded SHELL and PERL scripts # - Expand shell variables from %params and %ENV. # - Handle INCLUDE +# - Handle ?IF, ?ELSE, ?ENDIF # sub read_a_line(;$$$) { @@ -2019,6 +2037,52 @@ sub read_a_line(;$$$) { # $currentline = '', $currentlinenumber = 0, next if $currentline =~ /^\s*$/; # + # Line not blank -- Handle conditionals + # + if ( $currentline =~ /^\s*\?(IF\s+|ELSE|ENDIF)(.*)$/ ) { + my $rest = $2; + + $rest = '' unless supplied $rest; + + if ( $1 =~ /^IF/ ) { + fatal_error "Missing IF variable" unless $rest; + my $invert = $rest =~ s/^!\s*//; + + fatal_error "Invalid IF variable ($rest)" unless $rest =~ s/^\$// && $rest =~ /^\w+$/; + + push @ifstack, [ 'IF', $omitting, $currentlinenumber ]; + + if ( $rest eq '__IPV6' ) { + $omitting = $family == F_IPV4; + } elsif ( $rest eq '__IPV4' ) { + $omitting = $family == F_IPV6; + } else { + $omitting = ! ( exists $ENV{$rest} ? $ENV{$rest} : + exists $params{$rest} ? $params{$rest} : + exists $config{$rest} ? $config{$rest} : 0 ); + } + + $omitting = ! $omitting if $invert; + } elsif ( $1 eq 'ELSE' ) { + fatal_error "Invalid ?ELSE" unless $rest eq ''; + my ( $last, $omit, $lineno ) = @{pop @ifstack}; + fatal_error q(Unexpected "?ELSE" without matching ?IF) unless defined $last && $last eq 'IF'; + push @ifstack, [ 'ELSE', $omitting = ! $omit, $lineno ]; + } else { + fatal_error "Invalid ?END" unless $rest eq ''; + fatal_error q(Unexpected "?END" without matching ?IF or ?ELSE) if @ifstack <= $ifstack; + (my $last, $omitting ) = @{pop @ifstack}; + } + + $currentline='', next; + } + + if ( $omitting ) { + progress_message " OMITTED: $currentline"; + $currentline=''; + next; + } + # # Line not blank -- Handle any first-entry message/capabilities check # if ( $first_entry ) { @@ -2033,12 +2097,12 @@ sub read_a_line(;$$$) { # Must check for shell/perl before doing variable expansion # if ( $embedded_enabled ) { - if ( $currentline =~ s/^\s*(BEGIN\s+)?SHELL\s*;?// ) { + if ( $currentline =~ s/^\s*\??(BEGIN\s+)?SHELL\s*;?// ) { embedded_shell( $1 ); next; } - if ( $currentline =~ s/^\s*(BEGIN\s+)?PERL\s*\;?// ) { + if ( $currentline =~ s/^\s*\??(BEGIN\s+)?PERL\s*\;?// ) { embedded_perl( $1 ); next; } @@ -2050,7 +2114,7 @@ sub read_a_line(;$$$) { # expand_variables( $currentline ) if $expand_variables; - if ( $currentline =~ /^\s*INCLUDE\s/ ) { + if ( $currentline =~ /^\s*\??INCLUDE\s/ ) { my @line = split ' ', $currentline; @@ -2063,7 +2127,7 @@ sub read_a_line(;$$$) { fatal_error "Directory ($filename) not allowed in INCLUDE" if -d _; if ( -s _ ) { - push @includestack, [ $currentfile, $currentfilename, $currentlinenumber ]; + push @includestack, [ $currentfile, $currentfilename, $currentlinenumber, $ifstack = @ifstack ]; $currentfile = undef; do_open_file $filename; } else { @@ -2367,7 +2431,7 @@ sub load_kernel_modules( ) { my @suffixes = split /\s+/ , $config{MODULE_SUFFIX}; - while ( read_a_line ) { + while ( read_a_line1 ) { fatal_error "Invalid modules file entry" unless ( $currentline =~ /^loadmodule\s+([a-zA-Z]\w*)\s*(.*)$/ ); my ( $module, $arguments ) = ( $1, $2 ); unless ( $loadedmodules{ $module } ) { @@ -3235,7 +3299,7 @@ sub process_shorewall_conf( $$ ) { # # Don't expand shell variables or allow embedded scripting # - while ( read_a_line( 0, 0 ) ) { + while ( read_a_line1 ) { if ( $currentline =~ /^\s*([a-zA-Z]\w*)=(.*?)\s*$/ ) { my ($var, $val) = ($1, $2); diff --git a/Shorewall/manpages/shorewall-params.xml b/Shorewall/manpages/shorewall-params.xml index d1f9446ab..bd6558d92 100644 --- a/Shorewall/manpages/shorewall-params.xml +++ b/Shorewall/manpages/shorewall-params.xml @@ -23,7 +23,11 @@ Description - Assign any shell variables that you need in this file. + Assign any shell variables that you need in this file. The file is + always processed by /bin/sh or by the shell specified + through SHOREWALL_SHELL in shorewall.conf (5) so the full range of + shell capabilities may be used. It is suggested that variable names begin with an upper case letter to distinguish them from variables used internally within the Shorewall @@ -128,12 +132,13 @@ net eth0 130.252.100.255 routefilter,norfc1918 url="http://www.shorewall.net/configuration_file_basics.htm#Variables?">http://www.shorewall.net/configuration_file_basics.htm#Variables shorewall(8), shorewall-accounting(5), shorewall-actions(5), - shorewall-blacklist(5), shorewall-hosts(5), shorewall_interfaces(5), shorewall-ipsets(5), - shorewall-maclist(5), shorewall-masq(5), shorewall-nat(5), - shorewall-netmap(5), shorewall-policy(5), shorewall-providers(5), - shorewall-proxyarp(5), shorewall-rtrules(5), - shorewall-routestopped(5), shorewall-rules(5), shorewall.conf(5), shorewall-secmarks(5), - shorewall-tcclasses(5), shorewall-tcdevices(5), shorewall-tcrules(5), - shorewall-tos(5), shorewall-tunnels(5), shorewall-zones(5) + shorewall-blacklist(5), shorewall-hosts(5), shorewall_interfaces(5), + shorewall-ipsets(5), shorewall-maclist(5), shorewall-masq(5), + shorewall-nat(5), shorewall-netmap(5), shorewall-policy(5), + shorewall-providers(5), shorewall-proxyarp(5), shorewall-rtrules(5), + shorewall-routestopped(5), shorewall-rules(5), shorewall.conf(5), + shorewall-secmarks(5), shorewall-tcclasses(5), shorewall-tcdevices(5), + shorewall-tcrules(5), shorewall-tos(5), shorewall-tunnels(5), + shorewall-zones(5) diff --git a/Shorewall6/manpages/shorewall6-params.xml b/Shorewall6/manpages/shorewall6-params.xml index 04d25447f..8a663d4ad 100644 --- a/Shorewall6/manpages/shorewall6-params.xml +++ b/Shorewall6/manpages/shorewall6-params.xml @@ -23,7 +23,11 @@ Description - Assign any shell variables that you need in this file. + Assign any shell variables that you need in this file. The file is + always processed by /bin/sh or by the shell specified + through SHOREWALL_SHELL in shorewall6.conf (5) so the full range + of shell capabilities may be used. It is suggested that variable names begin with an upper case letter to distinguish them from variables used internally within the Shorewall @@ -130,8 +134,8 @@ net eth0 - dhcp,nosmurfs shorewall6(8), shorewall6-accounting(5), shorewall6-actions(5), shorewall6-blacklist(5), shorewall6-hosts(5), shorewall6-interfaces(5), shorewall6-maclist(5), shorewall6-policy(5), shorewall6-providers(5), - shorewall6-rtrules(5), shorewall6-routestopped(5), - shorewall6-rules(5), shorewall6.conf(5), shorewall6-secmarks(5), shorewall6-tcclasses(5), + shorewall6-rtrules(5), shorewall6-routestopped(5), shorewall6-rules(5), + shorewall6.conf(5), shorewall6-secmarks(5), shorewall6-tcclasses(5), shorewall6-tcdevices(5), shorewall6-tcrules(5), shorewall6-tos(5), shorewall6-tunnels(5), shorewall6-zones(5) diff --git a/docs/configuration_file_basics.xml b/docs/configuration_file_basics.xml index c132ad02b..04b0ab7a1 100644 --- a/docs/configuration_file_basics.xml +++ b/docs/configuration_file_basics.xml @@ -70,7 +70,10 @@ /etc/shorewall/params - use this file to - set shell variables that you will expand in other files. + set shell variables that you will expand in other files. It is + always processed by /bin/sh or by the shell specified through + SHOREWALL_SHELL in + /etc/shorewall/shorewall.conf. @@ -1044,6 +1047,16 @@ SHELL cat /etc/shorewall/rules.d/*.rules SECTION NEW SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true + + Beginning with Shorewall 4.5.2, in files other than + /etc/shorewall/params and + /etc/shorewall/conf, INCLUDE may be immediately + preceeded with '?' to signal that the line is a compiler directive and + not configuration data. + + Example: + + ?INCLUDE common.rules @@ -1427,6 +1440,112 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true +
+ Conditional Entries + + Beginning with Shorewall 4.5.2, lines in configuration files may be + conditionally included or omitted based on the setting of Shell variables. + + The general form is: + + ?IF $variable + +<lines to be included if $variable is non-empty and non-zero> + +?ELSE + +<lines to be omitted if $variable is non-empty and non-zero> + +?ENDIF + + The compiler predefines two special + variables that may only be used in ?IF + lines: + + + + __IPV4 + + + True if this is an IPv4 compilation + + + + + __IPV6 + + + True if this is an IPv6 compilation. + + + + + Unless variable is one of these + pre-defined ones, it is searched for in the compiler's environmental + variables, in variables set in /etc/shorewall/params, + and in options set in /etc/shorewall/shorewall.conf + in that order. If it is not found in any of those places, the + variable is assumed to have a value of 0 + (false). + + The setting in /etc/shorewall/params by be + overridden at runtime, provided the setting in + /etc/shorewall/params is done like this: + + [ -n "${variable:=0}" ] + + or like this: + + [ -n "${variable}" ] || variable=0 + + Either of those will set variable to 0 if it is not set to a + non-empty value in the environment. The setting can be overridden at + runtime: + + variable=1 shorewall restart -c # use -c to force recompilation if AUTOMAKE=Yes in /etc/shorewall/shorewall.conf + + The ?ELSE may be omitted if there are no lines to be omitted. + + The test may also be inverted using '!': + + ?IF ! $variable + +<lines to be omitted if $variable is non-empty and non-zero> + +?ELSE + +<lines to be included if $variable is non-empty and non-zero> + +?ENDIF + + Conditional entries may be nested but the number of ?IFs must match + the number of ?ENDs in any give file. INCLUDE + directives are ignored in omitted lines. + + ?IF $variable1 + +<lines to be included if $variable1 is non-empty and non-zero> + + ?IF $variable2 + +<lines to be included if $variable1 and $variable2 are non-empty and non-zero> + + ?ELSE + +<lines to be omitted if $variable1 is non-empty and non-zero and if $variable2 is empty or zero> + + ?ENDIF + +<lines to be included if $variable1 is non-empty and non-zero> + +?ELSE + +<lines to be omitted if $variable is non-empty and non-zero> + +?ENDIF +
+
Embedded Shell and Perl @@ -1442,21 +1561,27 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2> /dev/null || true/etc/shorewall/params and /etc/shorewall/shorewall.conf. - Embedded scripts can be either single-line or multi-line. Single - line scripts take one of the following forms: + Note:In this section, '[' and ']' + are meta-characters which indicate that what they enclose is optional and + may be omitted. + + Single line scripts take one of the following forms: - PERL <perl + [?]PERL <perl script> - SHELL <shell + [?]SHELL <shell script> + The optional '?' is allowed in Shorewall versions 4.5.2 and + later. + Shell scripts run in a child shell process and their output is piped back to the compiler which processes that output as if it were embedded at the point of the script. @@ -1501,18 +1626,14 @@ use Shorewall::Config ( qw/shorewall/ ); Multi-line scripts use one of the following forms:BEGIN SHELL + role="bold">[?]BEGIN SHELL <shell script> -END [ SHELL ]BEGIN PERL [;] +[?]END [ SHELL ][?]BEGIN PERL [;] <perl script> -END [ PERL ] [;] - - Note: The '[' and ']' above are - meta-characters which indicate that what they enclose is optional and may - be omitted. So you may follow PERL with a semicolon ( ';') or you may omit - the semicolon. +[?]END [ PERL ] [;]As above, the optional + leading '?' is allowed in Shorewall versions 4.5.2 and later.