2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-03 07:45:50 +00:00

Reworks the profile loading code to be able to parse profile from a

string we might have gotten from the network instead of requiring to
read it from a file and clean up the error handling (jmichael)
This commit is contained in:
Dominic Reynolds
2007-04-25 21:04:28 +00:00
parent 9bfe436dff
commit 43aa5f00f0

View File

@@ -2293,9 +2293,9 @@ sub checkIncludeSyntax($) {
if (-f "$profiledir/$id/$path") { if (-f "$profiledir/$id/$path") {
my $file = "$id/$path"; my $file = "$id/$path";
$file =~ s/$profiledir\///; $file =~ s/$profiledir\///;
my $err = loadinclude($file, \&printMessageErrorHandler); eval { loadinclude($file); };
if ($err ne 0) { if ( defined $@ && $@ ne "" ) {
push @$errors, $err; push @$errors, $@;
} }
} elsif (-d "$id/$path") { } elsif (-d "$id/$path") {
push @incdirs, "$id/$path"; push @incdirs, "$id/$path";
@@ -2318,7 +2318,7 @@ sub checkProfileSyntax ($) {
for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) { for my $file (grep { -f "$profiledir/$_" } readdir(SDDIR)) {
next if isSkippableFile($file); next if isSkippableFile($file);
my $err = readprofile("$profiledir/$file", \&printMessageErrorHandler); my $err = readprofile("$profiledir/$file", \&printMessageErrorHandler);
if (defined $err and $err ne 1) { if (defined $err and $err ne "") {
push @$errors, $err; push @$errors, $err;
} }
} }
@@ -2345,218 +2345,243 @@ sub readprofile ($$) {
my $file = shift; my $file = shift;
my $error_handler = shift; my $error_handler = shift;
if (open(SDPROF, "$file")) { if (open(SDPROF, "$file")) {
my ($profile, $hat, $in_contained_hat); local $/;
my $initial_comment = ""; my $data = <SDPROF>;
while (<SDPROF>) { close(SDPROF);
chomp;
# we don't care about blank lines eval {
next if /^\s*$/; my $profile_data = parse_profile_data($data, $file);
if ($profile_data) {
attach_profile_data(\%sd, $profile_data);
}
};
# start of a profile... # if there were errors loading the profile, call the error handler
if (m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) { if ($@) {
$@ =~ s/\n$//;
return &$error_handler($@);
}
} else {
$DEBUGGING && debug "readprofile: can't read $file - skipping";
}
}
# if we run into the start of a profile while we're already in a sub attach_profile_data {
# profile, something's wrong... my ($profiles, $profile_data) = @_;
if ($profile) {
return &$error_handler("$profile profile in $file contains syntax errors.");
}
# we hit the start of a profile, keep track of it... for my $p ( keys %$profile_data) {
$profile = $1; $profiles->{$p} = $profile_data->{$p};
my $flags = $2; }
}
sub parse_profile_data {
my ($data, $file) = @_;
my ($profile_data, $profile, $hat, $in_contained_hat);
my $initial_comment = "";
for (split(/\n/, $data)) {
chomp;
# we don't care about blank lines
next if /^\s*$/;
# start of a profile...
if (m/^\s*("??\/.+?"??)\s+(flags=\(.+\)\s+)*\{\s*$/) {
# if we run into the start of a profile while we're already in a
# profile, something's wrong...
if ($profile) {
die "$profile profile in $file contains syntax errors.\n";
}
# we hit the start of a profile, keep track of it...
$profile = $1;
my $flags = $2;
$in_contained_hat = 0;
# hat is same as profile name if we're not in a hat
($profile, $hat) = split /\^/, $profile;
# deal with whitespace in profile and hat names.
$profile = $1 if $profile =~ /^"(.+)"$/;
$hat = $1 if $hat && $hat =~ /^"(.+)"$/;
# if we run into old-style hat declarations mark the profile as
# changed so we'll write it out as new-style
if ($hat && $hat ne $profile) {
$changed{$profile} = 1;
}
$hat ||= $profile;
# keep track of profile flags
if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) {
$flags = $1;
$profile_data->{$profile}{$hat}{flags} = $flags;
}
$profile_data->{$profile}{$hat}{netdomain} = [];
# store off initial comment if they have one
$profile_data->{$profile}{$hat}{initial_comment} = $initial_comment if $initial_comment;
$initial_comment = "";
} elsif (m/^\s*\}\s*$/) { # end of a profile...
# if we hit the end of a profile when we're not in one, something's
# wrong...
if (not $profile) {
die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
}
if ($in_contained_hat) {
$hat = $profile;
$in_contained_hat = 0; $in_contained_hat = 0;
} else {
# hat is same as profile name if we're not in a hat # if we're finishing a profile, make sure that any required
($profile, $hat) = split /\^/, $profile; # infrastructure hats for this changehat application exist
for my $hatglob (keys %required_hats) {
# deal with whitespace in profile and hat names. if ($profile =~ /$hatglob/) {
$profile = $1 if $profile =~ /^"(.+)"$/; for my $hat (split(/\s+/, $required_hats{$hatglob})) {
$hat = $1 if $hat && $hat =~ /^"(.+)"$/; unless ($profile_data->{$profile}{$hat}) {
$profile_data->{$profile}{$hat} = { };
# if we run into old-style hat declarations mark the profile as # if we had to auto-instantiate a hat, we want to write out
# changed so we'll write it out as new-style # an updated version of the profile
if ($hat && $hat ne $profile) { $changed{$profile} = 1;
$changed{$profile} = 1;
}
$hat ||= $profile;
# keep track of profile flags
if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) {
$flags = $1;
$sd{$profile}{$hat}{flags} = $flags;
}
$sd{$profile}{$hat}{netdomain} = [];
# store off initial comment if they have one
$sd{$profile}{$hat}{initial_comment} = $initial_comment
if $initial_comment;
$initial_comment = "";
} elsif (m/^\s*\}\s*$/) { # end of a profile...
# if we hit the end of a profile when we're not in one,
# something's wrong...
if (not $profile) {
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
}
if ($in_contained_hat) {
$hat = $profile;
$in_contained_hat = 0;
} else {
# if we're finishing a profile, make sure that any required
# infrastructure hats for this changehat application exist
for my $hatglob (keys %required_hats) {
if ($profile =~ /$hatglob/) {
for my $hat (split(/\s+/, $required_hats{$hatglob})) {
unless ($sd{$profile}{$hat}) {
$sd{$profile}{$hat} = {};
# if we had to auto-instantiate a hat, we
# want to write out an updated version of
# the profile
$changed{$profile} = 1;
}
} }
} }
} }
# mark that we're outside of a profile now...
$profile = undef;
$initial_comment = "";
} }
} elsif (m/^\s*capability\s+(\S+)\s*,\s*$/) { # capability entry # mark that we're outside of a profile now...
if (not $profile) { $profile = undef;
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
}
my $capability = $1;
$sd{$profile}{$hat}{capability}{$capability} = 1;
} elsif (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { # boolean definition
} elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { # variable additions
} elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { # variable definitions
} elsif (m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { # conditional -- boolean
} elsif (m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- variable defined
} elsif (m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- boolean defined
} elsif (m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { # path entry
if (not $profile) {
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
}
my ($path, $mode) = ($1, $2);
# strip off any trailing spaces.
$path =~ s/\s+$//;
$path = $1 if $path =~ /^"(.+)"$/;
# make sure they don't have broken regexps in the profile
my $p_re = convert_regexp($path);
eval { "foo" =~ m/^$p_re$/; };
if ($@) {
return &$error_handler(sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path));
}
$sd{$profile}{$hat}{path}{$path} = $mode;
} elsif (m/^\s*#include <(.+)>\s*$/) { # include stuff
my $include = $1;
if ($profile) {
$sd{$profile}{$hat}{include}{$include} = 1;
} else {
unless (exists $variables{$file}) {
$variables{$file} = {};
}
$variables{$file}{ "#" . $include } = 1; # sorry
}
my $ret = loadinclude($include, $error_handler);
return $ret if ($ret != 0);
} elsif (/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) {
if (not $profile) {
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
}
# XXX - BUGBUGBUG - don't strip netdomain entries
unless ($sd{$profile}{$hat}{netdomain}) {
$sd{$profile}{$hat}{netdomain} = [];
}
# strip leading spaces and trailing comma
s/^\s+//;
s/,\s*$//;
# keep track of netdomain entries...
push @{ $sd{$profile}{$hat}{netdomain} }, $_;
} elsif (m/^\s*\^(\"?.+?)\s+(flags=\(.+\)\s+)*\{\s*$/) {
# start of a hat
# if we hit the start of a contained hat when we're not
# in a profile something is wrong...
if (not $profile) {
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
}
$in_contained_hat = 1;
# we hit the start of a hat inside the current profile
$hat = $1;
my $flags = $2;
# deal with whitespace in hat names.
$hat = $1 if $hat =~ /^"(.+)"$/;
# keep track of profile flags
if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) {
$flags = $1;
$sd{$profile}{$hat}{flags} = $flags;
}
$sd{$profile}{$hat}{path} = {};
$sd{$profile}{$hat}{netdomain} = [];
# store off initial comment if they have one
$sd{$profile}{$hat}{initial_comment} = $initial_comment
if $initial_comment;
$initial_comment = ""; $initial_comment = "";
} elsif (/^\s*\#/) {
# we only currently handle initial comments
if (not $profile) {
# ignore vim syntax highlighting lines
next if /^\s*\# vim:syntax/;
# ignore Last Modified: lines
next if /^\s*\# Last Modified:/;
$initial_comment .= "$_\n";
}
} else {
# we hit something we don't understand in a profile...
return &$error_handler(sprintf(gettext('%s contains syntax errors.'), $file));
} }
}
# if we're still in a profile when we hit the end of the file, it's bad } elsif (m/^\s*capability\s+(\S+)\s*,\s*$/) { # capability entry
if ($profile) { if (not $profile) {
return &$error_handler("Reached the end of $file while we were still inside the $profile profile."); die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
} }
close(SDPROF); my $capability = $1;
} else { $profile_data->{$profile}{$hat}{capability}{$capability} = 1;
$DEBUGGING && debug "readprofile: can't read $file - skipping";
} elsif (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { # boolean definition
} elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\+=\s*(.+)\s*$/) { # variable additions
} elsif (/^\s*(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*=\s*(.+)\s*$/) { # variable definitions
} elsif (m/^\s*if\s+(not\s+)?(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*\{\s*$/) { # conditional -- boolean
} elsif (m/^\s*if\s+(not\s+)?defined\s+(@\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- variable defined
} elsif (m/^\s*if\s+(not\s+)?defined\s+(\$\{?[[:alpha:]][[:alnum:]_]+\}?)\s*\{\s*$/) { # conditional -- boolean defined
} elsif (m/^\s*([\"\@\/].*)\s+(\S+)\s*,\s*$/) { # path entry
if (not $profile) {
die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
}
my ($path, $mode) = ($1, $2);
# strip off any trailing spaces.
$path =~ s/\s+$//;
$path = $1 if $path =~ /^"(.+)"$/;
# make sure they don't have broken regexps in the profile
my $p_re = convert_regexp($path);
eval { "foo" =~ m/^$p_re$/; };
if ($@) {
die sprintf(gettext('Profile %s contains invalid regexp %s.'), $file, $path) . "\n";
}
$profile_data->{$profile}{$hat}{path}{$path} = $mode;
} elsif (m/^\s*#include <(.+)>\s*$/) { # include stuff
my $include = $1;
if ($profile) {
$profile_data->{$profile}{$hat}{include}{$include} = 1;
} else {
unless (exists $variables{$file}) {
$variables{$file} = { };
}
$variables{$file}{"#" . $include} = 1; # sorry
}
# try to load the include...
my $ret = eval { loadinclude($include); };
# propagate errors up the chain
if ($@) { die $@; }
return $ret if ( $ret != 0 );
} elsif (/^\s*(tcp_connect|tcp_accept|udp_send|udp_receive)/) {
if (not $profile) {
die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
}
# XXX - BUGBUGBUG - don't strip netdomain entries
unless ($profile_data->{$profile}{$hat}{netdomain}) {
$profile_data->{$profile}{$hat}{netdomain} = [ ];
}
# strip leading spaces and trailing comma
s/^\s+//;
s/,\s*$//;
# keep track of netdomain entries...
push @{$profile_data->{$profile}{$hat}{netdomain}}, $_;
} elsif (m/^\s*\^(\"?.+?)\s+(flags=\(.+\)\s+)*\{\s*$/) { # start of a hat
# if we hit the start of a contained hat when we're not in a profile
# something is wrong...
if (not $profile) {
die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
}
$in_contained_hat = 1;
# we hit the start of a hat inside the current profile
$hat = $1;
my $flags = $2;
# deal with whitespace in hat names.
$hat = $1 if $hat =~ /^"(.+)"$/;
# keep track of profile flags
if ($flags && $flags =~ /^flags=\((.+)\)\s*$/) {
$flags = $1;
$profile_data->{$profile}{$hat}{flags} = $flags;
}
$profile_data->{$profile}{$hat}{path} = { };
$profile_data->{$profile}{$hat}{netdomain} = [];
# store off initial comment if they have one
$profile_data->{$profile}{$hat}{initial_comment} = $initial_comment if $initial_comment;
$initial_comment = "";
} elsif (/^\s*\#/) {
# we only currently handle initial comments
if (not $profile) {
# ignore vim syntax highlighting lines
next if /^\s*\# vim:syntax/;
# ignore Last Modified: lines
next if /^\s*\# Last Modified:/;
$initial_comment .= "$_\n";
}
} else {
# we hit something we don't understand in a profile...
die sprintf(gettext('%s contains syntax errors.'), $file) . "\n";
}
} }
# if we're still in a profile when we hit the end of the file, it's bad
if ($profile) {
die "Reached the end of $file while we were still inside the $profile profile.\n";
}
return $profile_data;
} }
sub escape ($) { sub escape ($) {
@@ -2573,106 +2598,158 @@ sub escape ($) {
return $dangerous; return $dangerous;
} }
sub writeheader ($$$$) { sub writeheader ($$) {
my ($fh, $profile, $hat, $indent) = @_; my ($profile_data, $name) = @_;
my @data;
# deal with whitespace in profile names... # deal with whitespace in profile names...
my $p = $profile; $name = "\"$name\"" if $name =~ /\s/;
$p = "\"$p\"" if $p =~ /\s/;
if ($sd{$profile}{$hat}{flags}) { if ($profile_data->{flags}) {
print $fh "$p flags=($sd{$profile}{$hat}{flags}) {\n"; push @data, "$name flags=($profile_data->{flags}) {";
} else { } else {
print $fh "$p {\n"; push @data, "$name {";
} }
return @data;
} }
sub writeincludes ($$$$) { sub writeincludes ($) {
my ($fh, $profile, $hat, $indent) = @_; my $profile_data = shift;
my @data;
# dump out the includes # dump out the includes
if (exists $sd{$profile}{$hat}{include}) { if (exists $profile_data->{include}) {
for my $include (sort keys %{ $sd{$profile}{$hat}{include} }) { for my $include (sort keys %{$profile_data->{include}}) {
print $fh "$indent #include <$include>\n"; push @data, " #include <$include>";
} }
print $fh "\n" if keys %{ $sd{$profile}{$hat}{include} }; push @data, "" if keys %{$profile_data->{include}};
} }
return @data;
} }
sub writecapabilities ($$$$) { sub writecapabilities ($) {
my ($fh, $profile, $hat, $indent) = @_; my $profile_data = shift;
my @data;
# dump out the capability entries... # dump out the capability entries...
if (exists $sd{$profile}{$hat}{capability}) { if (exists $profile_data->{capability}) {
for my $capability (sort keys %{ $sd{$profile}{$hat}{capability} }) { for my $capability (sort keys %{$profile_data->{capability}}) {
print $fh "$indent capability $capability,\n"; push @data, " capability $capability,";
} }
print $fh "\n" if keys %{ $sd{$profile}{$hat}{capability} }; push @data, "" if keys %{$profile_data->{capability}};
} }
return @data;
} }
sub writenetdomain ($$$$) { sub writenetdomain ($) {
my ($fh, $profile, $hat, $indent) = @_; my $profile_data = shift;
my @data;
# dump out the netdomain entries... # dump out the netdomain entries...
if (exists $sd{$profile}{$hat}{netdomain}) { if (exists $profile_data->{netdomain}) {
for my $nd (sort @{ $sd{$profile}{$hat}{netdomain} }) { for my $nd (sort @{$profile_data->{netdomain}}) {
print $fh "$indent $nd,\n"; push @data, " $nd,";
} }
print $fh "\n" if @{ $sd{$profile}{$hat}{netdomain} }; push @data, "" if @{$profile_data->{netdomain}};
} }
return @data;
} }
sub writepaths ($$$$) { sub writepaths ($) {
my ($fh, $profile, $hat, $indent) = @_; my $profile_data = shift;
if (exists $sd{$profile}{$hat}{path}) { my @data;
for my $path (sort keys %{ $sd{$profile}{$hat}{path} }) { if (exists $profile_data->{path}) {
my $mode = $sd{$profile}{$hat}{path}{$path}; for my $path (sort keys %{$profile_data->{path}}) {
my $mode = $profile_data->{path}{$path};
# strip out any fake access() modes that might have slipped through # strip out any fake access() modes that might have slipped through
$mode =~ s/X//g; $mode =~ s/X//g;
# deal with whitespace in path names # deal with whitespace in path names
if ($path =~ /\s/) { if ($path =~ /\s/) {
print $fh "$indent \"$path\" $mode,\n"; push @data, " \"$path\" $mode,";
} else { } else {
print $fh "$indent $path $mode,\n"; push @data, " $path $mode,";
} }
} }
} }
return @data;
} }
sub writepiece ($$) { sub writepiece ($$) {
my ($sdprof, $profile) = @_; my ($profile_data, $name) = @_;
writeheader($sdprof, $profile, $profile, ""); my @data;
writeincludes($sdprof, $profile, $profile, ""); push @data, writeheader($profile_data->{$name}, $name);
writecapabilities($sdprof, $profile, $profile, ""); push @data, writeincludes($profile_data->{$name});
writenetdomain($sdprof, $profile, $profile, ""); push @data, writecapabilities($profile_data->{$name});
writepaths($sdprof, $profile, $profile, ""); push @data, writenetdomain($profile_data->{$name});
push @data, writepaths($profile_data->{$name});
for my $hat (grep { $_ ne $profile } sort keys %{ $sd{$profile} }) { for my $hat (grep { $_ ne $name } sort keys %{$profile_data}) {
push @data, "";
# deal with whitespace in profile names... push @data, map { " $_" } writeheader($profile_data->{$hat}, "^$hat");
my $h = $hat; push @data, map { " $_" } writeincludes($profile_data->{$hat});
$h = "\"$h\"" if $h =~ /\s/; push @data, map { " $_" } writecapabilities($profile_data->{$hat});
push @data, map { " $_" } writenetdomain($profile_data->{$hat});
if ($sd{$profile}{$hat}{flags}) { push @data, map { " $_" } writepaths($profile_data->{$hat});
print $sdprof "\n ^$h flags=($sd{$profile}{$hat}{flags}) {\n"; push @data, " }";
} else {
print $sdprof "\n ^$h {\n";
}
writeincludes($sdprof, $profile, $hat, " ");
writecapabilities($sdprof, $profile, $hat, " ");
writenetdomain($sdprof, $profile, $hat, " ");
writepaths($sdprof, $profile, $hat, " ");
print $sdprof " }\n";
} }
print $sdprof "}\n"; push @data, "}";
return @data;
}
sub serialize_profile {
my ($profile_data, $name, $include_metadata) = @_;
my $string = "";
if ($include_metadata) {
# keep track of when the file was last updated
$string .= "# Last Modified: " . localtime(time) . "\n";
}
# print out initial comment
if ($profile_data->{$name}{initial_comment}) {
my $comment = $profile_data->{$name}{initial_comment};
$comment =~ s/\\n/\n/g;
$string .= "$comment\n";
}
# XXX - FIX THIS
#
# # dump variables defined in this file
# if ($variables{$filename}) {
# for my $var (sort keys %{$variables{$filename}}) {
# if ($var =~ m/^@/) {
# my @values = sort @{$variables{$filename}{$var}};
# @values = map { escape($_) } @values;
# my $values = join (" ", @values);
# print SDPROF "$var = ";
# print SDPROF $values;
# } elsif ($var =~ m/^\$/) {
# print SDPROF "$var = ";
# print SDPROF ${$variables{$filename}{$var}};
# } elsif ($var =~ m/^\#/) {
# my $inc = $var;
# $inc =~ s/^\#//;
# print SDPROF "#include <$inc>";
# }
# print SDPROF "\n";
# }
# }
$string .= join("\n", writepiece($profile_data, $name));
return "$string\n";
} }
sub writeprofile ($) { sub writeprofile ($) {
@@ -2682,48 +2759,13 @@ sub writeprofile ($) {
my $filename = getprofilefilename($profile); my $filename = getprofilefilename($profile);
open(SDPROF, ">$filename") open(SDPROF, ">$filename") or fatal_error "Can't write new AppArmor profile $filename: $!";
or fatal_error "Can't write new AppArmor profile $filename: $!"; my $profile_string = serialize_profile($sd{$profile}, $profile, 1);
print SDPROF $profile_string;
# stick in a vim mode line to turn on AppArmor syntax highlighting
print SDPROF "# vim:syntax=apparmor\n";
# keep track of when the file was last updated
print SDPROF "# Last Modified: " . localtime(time) . "\n";
# print out initial comment
if ($sd{$profile}{$profile}{initial_comment}) {
$sd{$profile}{$profile}{initial_comment} =~ s/\\n/\n/g;
print SDPROF $sd{$profile}{$profile}{initial_comment};
print SDPROF "\n";
}
# dump variables defined in this file
if ($variables{$filename}) {
for my $var (sort keys %{ $variables{$filename} }) {
if ($var =~ m/^@/) {
my @values = sort @{ $variables{$filename}{$var} };
@values = map { escape($_) } @values;
my $values = join(" ", @values);
print SDPROF "$var = ";
print SDPROF $values;
} elsif ($var =~ m/^\$/) {
print SDPROF "$var = ";
print SDPROF ${ $variables{$filename}{$var} };
} elsif ($var =~ m/^\#/) {
my $inc = $var;
$inc =~ s/^\#//;
print SDPROF "#include <$inc>";
}
print SDPROF "\n";
}
}
print SDPROF "\n";
writepiece(\*SDPROF, $profile);
close(SDPROF); close(SDPROF);
# mark the profile as up-to-date
delete $changed{$profile};
} }
sub getprofileflags { sub getprofileflags {
@@ -2746,6 +2788,7 @@ sub getprofileflags {
return $flags; return $flags;
} }
sub matchliteral { sub matchliteral {
my ($sd_regexp, $literal) = @_; my ($sd_regexp, $literal) = @_;
@@ -2774,9 +2817,31 @@ sub reload ($) {
system("/bin/cat '$filename' | $parser -I$profiledir -r >/dev/null 2>&1"); system("/bin/cat '$filename' | $parser -I$profiledir -r >/dev/null 2>&1");
} }
sub read_include_from_file {
my $which = shift;
my $data;
if (open(INCLUDE, "$profiledir/$which")) {
local $/;
$data = <INCLUDE>;
close(INCLUDE);
}
return $data;
}
sub get_include_data {
my $which = shift;
my $data = read_include_from_file($which);
unless($data) {
fatal_error "Can't find include file $which: $!";
}
return $data;
}
sub loadinclude { sub loadinclude {
my $which = shift; my $which = shift;
my $error_handler = shift;
# don't bother loading it again if we already have # don't bother loading it again if we already have
return 0 if $include{$which}; return 0 if $include{$which};
@@ -2784,11 +2849,8 @@ sub loadinclude {
my @loadincludes = ($which); my @loadincludes = ($which);
while (my $incfile = shift @loadincludes) { while (my $incfile = shift @loadincludes) {
# load the include from the directory we found earlier... my $data = get_include_data($incfile);
open(INCLUDE, "$profiledir/$incfile") for (split(/\n/, $data)) {
or fatal_error "Can't find include file $incfile: $!";
while (<INCLUDE>) {
chomp; chomp;
if (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) { if (/^\s*(\$\{?[[:alpha:]][[:alnum:]_]*\}?)\s*=\s*(true|false)\s*$/i) {
@@ -2819,7 +2881,8 @@ sub loadinclude {
my $p_re = convert_regexp($path); my $p_re = convert_regexp($path);
eval { "foo" =~ m/^$p_re$/; }; eval { "foo" =~ m/^$p_re$/; };
if ($@) { if ($@) {
return &$error_handler(sprintf(gettext('Include file %s contains invalid regexp %s.'), $incfile, $path)); die sprintf(gettext('Include file %s contains invalid regexp %s.'),
$incfile, $path) . "\n";
} }
$include{$incfile}{path}{$path} = $mode; $include{$incfile}{path}{$path} = $mode;
@@ -2843,7 +2906,8 @@ sub loadinclude {
next if /^\s*\#/; next if /^\s*\#/;
# we hit something we don't understand in a profile... # we hit something we don't understand in a profile...
return &$error_handler(sprintf(gettext('Include file %s contains syntax errors or is not a valid #include file.'), $incfile)); die sprintf(gettext('Include file %s contains syntax errors or is not a valid #include file.'),
$incfile) . "\n";
} }
} }
close(INCLUDE); close(INCLUDE);
@@ -2883,7 +2947,8 @@ sub matchincludes {
# scan the include fragments for this profile looking for matches # scan the include fragments for this profile looking for matches
my @includelist = keys %{ $frag->{include} }; my @includelist = keys %{ $frag->{include} };
while (my $include = shift @includelist) { while (my $include = shift @includelist) {
loadinclude($include, \&fatal_error); my $ret = eval { loadinclude($include); };
if ($@) { fatal_error $@; }
my ($cm, @m) = rematchfrag($include{$include}, $path); my ($cm, @m) = rematchfrag($include{$include}, $path);
if ($cm) { if ($cm) {
$combinedmode .= $cm; $combinedmode .= $cm;
@@ -2984,7 +3049,8 @@ sub loadincludes {
if (-f "$profiledir/$id/$path") { if (-f "$profiledir/$id/$path") {
my $file = "$id/$path"; my $file = "$id/$path";
$file =~ s/$profiledir\///; $file =~ s/$profiledir\///;
loadinclude($file, \&fatal_error); my $ret = eval { loadinclude($file); };
if ($@) { fatal_error $@; }
} elsif (-d "$id/$path") { } elsif (-d "$id/$path") {
push @incdirs, "$id/$path"; push @incdirs, "$id/$path";
} }