pull/643/merge
Matthias Urlichs 2026-03-31 15:52:16 +02:00 committed by GitHub
commit 4e3988cd96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 95 additions and 15 deletions

87
btrbk
View File

@ -2093,7 +2093,7 @@ sub system_urandom($;$) {
binmode URANDOM;
my $rand;
my $rlen = read(URANDOM, $rand, $size);
close(FILE);
close(URANDOM);
unless(defined($rand) && ($rlen == $size)) {
ERROR "Failed to read from /dev/urandom: $!";
return undef;
@ -4313,17 +4313,29 @@ sub _config_file(@) {
return undef;
}
sub parse_config($)
# Recursively parse a single config file, following any "include" directives.
# Returns the updated $cur context on success, undef on error.
# $seen is a hashref mapping realpath -> 1 for files currently on the include
# stack, used to detect circular includes.
sub _parse_config_file
{
my $file = shift;
return undef unless($file);
my ($file, $root, $cur, $seen) = @_;
$seen //= {};
my $root = init_config(SRC_FILE => $file);
my $cur = $root;
my $real = abs_path($file) // $file;
if($seen->{$real}) {
ERROR "Circular include detected for \"$file\"";
return undef;
}
$seen->{$real} = 1;
TRACE "config: open configuration file: $file" if($do_trace);
open(FILE, '<', $file) or die $!;
while (<FILE>) {
open(my $fh, '<', $file) or do {
ERROR "Cannot open configuration file \"$file\": $!";
delete $seen->{$real};
return undef;
};
while(<$fh>) {
chomp;
s/((?:[^"'#]*(?:"[^"]*"|'[^']*'))*[^"'#]*)#.*/$1/; # remove comments
next if /^\s*$/; # ignore empty lines
@ -4332,19 +4344,64 @@ sub parse_config($)
TRACE "config: parsing line $. with context=$cur->{CONTEXT}: \"$_\"" if($do_trace);
unless(/^([a-zA-Z_]+)(?:\s+(.*))?$/) {
ERROR "Parse error in \"$file\" line $.";
$root = undef;
last;
close $fh;
delete $seen->{$real};
return undef;
}
my ($key, $value) = (lc($1), $2 // "");
if($key eq "include") {
$value =~ s/^"(.*)"$/$1/;
$value =~ s/^'(.*)'$/$1/;
# resolve a relative pattern against the directory of the including file
unless($value =~ m{^/}) {
(my $dir = $file) =~ s{/[^/]*$}{};
$dir = "." if($dir eq $file); # $file had no slash; use current directory
$value = "$dir/$value";
}
my @inc_files;
for my $m (sort glob($value)) {
if(-d $m) {
push @inc_files, sort glob("$m/*.conf");
} elsif(-e $m) {
push @inc_files, $m;
}
}
for my $inc_file (@inc_files) {
TRACE "config: including file: $inc_file (from \"$file\" line $.)" if($do_trace);
$cur = _parse_config_file($inc_file, $root, $cur, $seen);
unless(defined($cur)) {
close $fh;
delete $seen->{$real};
return undef;
}
}
} else {
unless($cur = parse_config_line($cur, $key, $value, error_statement => "in \"$file\" line $.")) {
close $fh;
delete $seen->{$real};
return undef;
}
unless($cur = parse_config_line($cur, lc($1), $2 // "", error_statement => "in \"$file\" line $.")) {
$root = undef;
last;
}
TRACE "line processed: new context=$cur->{CONTEXT}" if($do_trace);
}
close FILE || ERROR "Failed to close configuration file: $!";
close $fh || ERROR "Failed to close configuration file: $!";
delete $seen->{$real};
return $cur;
}
sub parse_config($)
{
my $file = shift;
return undef unless($file);
my $root = init_config(SRC_FILE => $file);
unless(_parse_config_file($file, $root, $root, {})) {
return undef;
}
_config_propagate_target($root);
return $root;
}

View File

@ -37,6 +37,29 @@ Blank lines are ignored. A hash character (#) starts a comment
extending until end of line.
INCLUDE FILES
-------------
*include* <pattern>::
Include additional configuration files matching the shell glob
'<pattern>'. All matching files are processed in sorted order.
If '<pattern>' matches a directory, it is treated as if
+'<pattern>/*.conf'+ had been specified, i.e. all files ending in
+.conf+ directly inside that directory are included.
+
--
A relative '<pattern>' is resolved relative to the directory of the
file containing the 'include' directive.
Circular includes are detected and reported as an error.
Example:
include /etc/btrbk/btrbk.d/*.conf
include /etc/btrbk/btrbk.d
--
SECTIONS
--------