Allow conditional compilation

Signed-off-by: Tom Eastep <teastep@shorewall.net>
This commit is contained in:
Tom Eastep 2012-03-16 08:16:12 -07:00
parent 8e413a7bf0
commit ab13fbe95e
4 changed files with 236 additions and 42 deletions

View File

@ -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 <filename>
# - 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);

View File

@ -23,7 +23,11 @@
<refsect1>
<title>Description</title>
<para>Assign any shell variables that you need in this file.</para>
<para>Assign any shell variables that you need in this file. The file is
always processed by <filename>/bin/sh</filename> or by the shell specified
through SHOREWALL_SHELL in <ulink
url="shorewall.conf.html">shorewall.conf</ulink> (5) so the full range of
shell capabilities may be used.</para>
<para>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</programlisting>
url="http://www.shorewall.net/configuration_file_basics.htm#Variables?">http://www.shorewall.net/configuration_file_basics.htm#Variables</ulink></para>
<para>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)</para>
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)</para>
</refsect1>
</refentry>

View File

@ -23,7 +23,11 @@
<refsect1>
<title>Description</title>
<para>Assign any shell variables that you need in this file.</para>
<para>Assign any shell variables that you need in this file. The file is
always processed by <filename>/bin/sh</filename> or by the shell specified
through SHOREWALL_SHELL in <ulink
url="shorewall6.conf.html">shorewall6.conf</ulink> (5) so the full range
of shell capabilities may be used.</para>
<para>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</programlisting>
<para>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)</para>
</refsect1>

View File

@ -70,7 +70,10 @@
<listitem>
<para><filename>/etc/shorewall/params</filename> - use this file to
set shell variables that you will expand in other files.</para>
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
<filename>/etc/shorewall/shorewall.conf.</filename></para>
</listitem>
<listitem>
@ -1044,6 +1047,16 @@ SHELL cat /etc/shorewall/rules.d/*.rules</programlisting></para>
<programlisting>SECTION NEW
SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting>
<para>Beginning with Shorewall 4.5.2, in files other than
<filename>/etc/shorewall/params</filename> and
<filename>/etc/shorewall/conf</filename>, INCLUDE may be immediately
preceeded with '?' to signal that the line is a compiler directive and
not configuration data.</para>
<para>Example:</para>
<programlisting>?INCLUDE common.rules</programlisting>
</example>
</section>
@ -1427,6 +1440,112 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
</note>
</section>
<section id="Conditional">
<title>Conditional Entries</title>
<para>Beginning with Shorewall 4.5.2, lines in configuration files may be
conditionally included or omitted based on the setting of <link
linkend="Variables">Shell variables</link>.</para>
<para>The general form is:</para>
<programlisting>?IF <replaceable>$variable
</replaceable>&lt;lines to be included if $variable is non-empty and non-zero&gt;
?ELSE
&lt;lines to be omitted if $variable is non-empty and non-zero&gt;
?ENDIF</programlisting>
<para>The compiler predefines two special
<replaceable>variable</replaceable>s that may only be used in ?IF
lines:</para>
<variablelist>
<varlistentry>
<term>__IPV4</term>
<listitem>
<para>True if this is an IPv4 compilation</para>
</listitem>
</varlistentry>
<varlistentry>
<term>__IPV6</term>
<listitem>
<para>True if this is an IPv6 compilation.</para>
</listitem>
</varlistentry>
</variablelist>
<para>Unless <replaceable>variable</replaceable> is one of these
pre-defined ones, it is searched for in the compiler's environmental
variables, in variables set in <filename>/etc/shorewall/params</filename>,
and in options set in <filename>/etc/shorewall/shorewall.conf</filename>
in that order. If it is not found in any of those places, the
<replaceable>variable</replaceable> is assumed to have a value of 0
(false).</para>
<para>The setting in <filename>/etc/shorewall/params</filename> by be
overridden at runtime, provided the setting in
<filename>/etc/shorewall/params</filename> is done like this:</para>
<programlisting>[ -n "${<replaceable>variable</replaceable>:=0}" ]</programlisting>
<para>or like this:</para>
<programlisting>[ -n "${<replaceable>variable</replaceable>}" ] || <replaceable>variable</replaceable>=0</programlisting>
<para>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:</para>
<programlisting><replaceable>variable</replaceable>=1 shorewall restart -c # use -c to force recompilation if AUTOMAKE=Yes in /etc/shorewall/shorewall.conf</programlisting>
<para>The ?ELSE may be omitted if there are no lines to be omitted.</para>
<para>The test may also be inverted using '!':</para>
<programlisting>?IF ! <replaceable>$variable
</replaceable>&lt;lines to be omitted if $variable is non-empty and non-zero&gt;
?ELSE
&lt;lines to be included if $variable is non-empty and non-zero&gt;
?ENDIF</programlisting>
<para>Conditional entries may be nested but the number of ?IFs must match
the number of ?ENDs in any give file. <link linkend="INCLUDE">INCLUDE
directives</link> are ignored in omitted lines.</para>
<programlisting>?IF <replaceable>$variable1
</replaceable>&lt;lines to be included if $variable1 is non-empty and non-zero&gt;
?IF $variable2
&lt;lines to be included if $variable1 and $variable2 are non-empty and non-zero&gt;
?ELSE
&lt;lines to be omitted if $variable1 is non-empty and non-zero and if $variable2 is empty or zero&gt;
?ENDIF
<replaceable>
</replaceable>&lt;lines to be included if $variable1 is non-empty and non-zero&gt;
?ELSE
&lt;lines to be omitted if $variable is non-empty and non-zero&gt;
?ENDIF</programlisting>
</section>
<section id="Embedded">
<title>Embedded Shell and Perl</title>
@ -1442,21 +1561,27 @@ SHELL cat /etc/shorewall/rules.d/*.rules 2&gt; /dev/null || true</programlisting
configuration files except <filename>/etc/shorewall/params</filename> and
<filename>/etc/shorewall/shorewall.conf</filename>.</para>
<para>Embedded scripts can be either single-line or multi-line. Single
line scripts take one of the following forms:</para>
<para><emphasis role="bold">Note:</emphasis>In this section, '[' and ']'
are meta-characters which indicate that what they enclose is optional and
may be omitted.</para>
<para>Single line scripts take one of the following forms:</para>
<itemizedlist>
<listitem>
<para><emphasis role="bold">PERL</emphasis> &lt;<emphasis>perl
<para><emphasis role="bold">[?]PERL</emphasis> &lt;<emphasis>perl
script</emphasis>&gt;</para>
</listitem>
<listitem>
<para><emphasis role="bold">SHELL</emphasis> &lt;<emphasis>shell
<para><emphasis role="bold">[?]SHELL</emphasis> &lt;<emphasis>shell
script</emphasis>&gt;</para>
</listitem>
</itemizedlist>
<para>The optional '?' is allowed in Shorewall versions 4.5.2 and
later.</para>
<para>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.</para>
@ -1501,18 +1626,14 @@ use Shorewall::Config ( qw/shorewall/ );</programlisting>
</orderedlist>
<para>Multi-line scripts use one of the following forms:<programlisting><emphasis
role="bold">BEGIN SHELL</emphasis>
role="bold">[?]BEGIN SHELL</emphasis>
&lt;<emphasis>shell script</emphasis>&gt;
<emphasis role="bold">END</emphasis> [ <emphasis role="bold">SHELL</emphasis> ]</programlisting><programlisting><emphasis
role="bold">BEGIN PERL</emphasis> [;]
<emphasis role="bold">[?]END</emphasis> [ <emphasis role="bold">SHELL</emphasis> ]</programlisting><programlisting><emphasis
role="bold">[?]BEGIN PERL</emphasis> [;]
&lt;<emphasis>perl script</emphasis>&gt;
<emphasis role="bold">END</emphasis> [ <emphasis role="bold">PERL</emphasis> ] [<emphasis
role="bold">;</emphasis>]</programlisting></para>
<para><emphasis role="bold">Note: </emphasis>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.</para>
<emphasis role="bold">[?]END</emphasis> [ <emphasis role="bold">PERL</emphasis> ] [<emphasis
role="bold">;</emphasis>]</programlisting>As above, the optional
leading '?' is allowed in Shorewall versions 4.5.2 and later.</para>
</section>
<section id="dnsnames">