diff --git a/nginx/nginx-lib.pl b/nginx/nginx-lib.pl index fd6055628..c36dbb80e 100644 --- a/nginx/nginx-lib.pl +++ b/nginx/nginx-lib.pl @@ -551,8 +551,11 @@ if ($lock_all_config_files_depth) { my $main = &resolve_links($config{'nginx_config'}) || $config{'nginx_config'}; my @files = ($main); my %locked; -while (@files) { +# Repeat until a fresh parse finds no additional config files. +while (1) { + my $new_files = 0; foreach my $f (@files) { + next if ($locked{$f}); my $pid = &test_lock($f); if ($pid && $pid == $$) { # Keep a lock taken by the caller outside our unlock list. @@ -564,12 +567,15 @@ while (@files) { &error("Failed to lock Nginx config file $f"); } $locked{$f} = 1; + $new_files++; &unflush_file_lines($f); } - # Raw config edits lock included files independently. Re-read after - # acquiring each batch and lock any newly discovered includes. + last if (!$new_files); + + # An included file may change while this process waits for its lock. + # Reparse after each batch to find any newly included files. &flush_config_cache(); - @files = grep { !$locked{$_} } &unique(&get_all_config_files(), + @files = &unique(&get_all_config_files(), $parent ? &get_all_config_files($parent) : ()); } $lock_all_config_files_depth = 1; diff --git a/nginx/t/config-locks.t b/nginx/t/config-locks.t index 3fa9110d6..8f8fbb069 100644 --- a/nginx/t/config-locks.t +++ b/nginx/t/config-locks.t @@ -163,4 +163,23 @@ subtest 'included files are refreshed after waiting for their locks' => sub { main::unlock_all_config_files(); }; +subtest 'a parent block can span included files' => sub { + write_text($included, "listen 80;\n"); + write_text($conf, + "http {\n". + " server {\n". + " server_name parent.invalid;\n". + " include $included;\n". + " }\n". + "}\n"); + main::flush_config_cache(); + my $parent = server('parent.invalid'); + is_deeply([sort(main::get_all_config_files($parent))], + [sort($conf, $included)], 'parent spans both config files'); + main::lock_all_config_files($parent); + ok(-e "$conf.lock" && -e "$included.lock", + 'parent lock covers both config files'); + main::unlock_all_config_files(); +}; + done_testing();