From 22c580aea11bc8305801f57c990af91e91dba537 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Fri, 2 Oct 2026 16:35:15 +0200 Subject: [PATCH] Update Apache config test to keep old behavior when ServerName is set --- apache/apache-lib.pl | 42 +++++++++++++++++++++-------------------- apache/t/test-config.t | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/apache/apache-lib.pl b/apache/apache-lib.pl index dfbe6343e..83280a967 100755 --- a/apache/apache-lib.pl +++ b/apache/apache-lib.pl @@ -1994,12 +1994,6 @@ sub test_config return undef if ($httpd_modules{'core'} < 1.301); my $okre = qr/(syntax|Checking).*\s+ok/i; -# Without a global ServerName, Apache looks up the system hostname in DNS -# while loading the config, which stalls the test when DNS is slow. Apache -# applies -C directives before reading the config files, so this placeholder -# never overrides a ServerName set in the config. -my $noname = "-C \"ServerName localhost\""; - # Build the httpd command from the configured paths my $httpd = &find_httpd(); my $httpdcmd = "\"$httpd\" -d \"$config{'httpd_dir'}\""; @@ -2010,33 +2004,41 @@ foreach my $d (&get_httpd_defines()) { $httpdcmd .= " -D$d"; } -# Pick the standard test command, and the same test without the DNS lookup -my ($cmd, $fastcmd, $apachectl); +# Pick the standard test command +my ($cmd, $apachectl); if ($config{'test_apachectl'} && -x &translate_filename($config{'apachectl_path'})) { # Test with apachectl, which may set up environment the config needs $apachectl = "\"$config{'apachectl_path'}\""; $cmd = "$apachectl configtest"; - $fastcmd = "$apachectl $noname -t"; } else { # Test with httpd $cmd = "$httpdcmd -t"; - $fastcmd = "$httpdcmd $noname -t"; } -# Try the test without the DNS lookup first -my $out = &backquote_command("$fastcmd 2>&1"); -if ($apachectl && $httpd && $out =~ /no\s+longer\s+supported/i) { - # Red Hat's apachectl refuses extra arguments, and its configtest - # just runs httpd -t, so test with httpd instead - $out = &backquote_command("$httpdcmd $noname -t 2>&1"); +# Without a global ServerName, Apache looks up the system hostname in DNS +# while loading the config, which stalls the test when DNS is slow. So first +# try the test with a placeholder ServerName. Apache applies -C directives +# before reading the config files, so a ServerName that this check misses, +# such as one inside an IfModule section, still takes effect. +my $conf = &get_config(); +if ($conf && !&find_directive("ServerName", $conf)) { + my $noname = "-C \"ServerName localhost\""; + my $out = &backquote_command( + ($apachectl ? "$apachectl $noname -t" : "$httpdcmd $noname -t"). + " 2>&1"); + if ($apachectl && $httpd && $out =~ /no\s+longer\s+supported/i) { + # Red Hat's apachectl refuses extra arguments, and its + # configtest just runs httpd -t, so test with httpd instead + $out = &backquote_command("$httpdcmd $noname -t 2>&1"); + } + return undef if ($out =~ $okre); } -return undef if ($out =~ $okre); -# The quick test failed, so run the standard test in case the extra -# arguments, rather than the config, caused the failure -$out = &backquote_command("$cmd 2>&1"); +# Run the standard test. This also runs when the quick test failed, in case +# the extra arguments, rather than the config, caused the failure. +my $out = &backquote_command("$cmd 2>&1"); if ($out && $out !~ $okre) { return $out; } diff --git a/apache/t/test-config.t b/apache/t/test-config.t index 017adf0c8..203b7d8ea 100644 --- a/apache/t/test-config.t +++ b/apache/t/test-config.t @@ -120,6 +120,23 @@ require File::Spec->catfile($root, 'apache', 'apache-lib.pl'); $main::httpd_modules{'core'} = 2.4; } +# set_apache_conf(text) +# Writes the main Apache config file and clears the cached copy +sub set_apache_conf +{ +my ($text) = @_; +write_text($apache_conf, $text); +main::flush_config_cache(); +} + +# A ServerName inside a virtual host is not global, so Apache still looks up +# the system hostname +my $no_global_name = "Listen 80\n". + "\n". + " ServerName vhost.example\n". + "\n"; +set_apache_conf($no_global_name); + # Expected log lines for the fake commands my $noname = 'ServerName localhost'; my $direct_fast = join('|', 'httpd', '-d', $apache_root, '-f', $apache_conf, @@ -202,6 +219,32 @@ subtest 'unknown apachectl falls back to configtest' => sub { 'standard test runs when apachectl rejects the arguments'); }; +subtest 'global ServerName uses only the standard test' => sub { + set_apache_conf("ServerName www.example.com\n".$no_global_name); + my ($err, $cmds) = run_test(0, undef, 'ok'); + is($err, undef, 'valid config passes'); + is_deeply($cmds, [ $direct_std ], 'httpd runs without a placeholder'); + + ($err, $cmds) = run_test(1, $redhat_ctl, 'error'); + like($err, qr/Syntax error on line 2/, 'error output is returned'); + is_deeply($cmds, [ $ctl_std, $httpd_std ], + 'apachectl configtest runs as before'); + set_apache_conf($no_global_name); +}; + +subtest 'global ServerName in an included file is found' => sub { + # Webmin expands includes with glob(), which splits paths on spaces + my $inc = File::Spec->catfile($tmp, 'servername.conf'); + write_text($inc, "ServerName www.example.com\n"); + set_apache_conf("Include $inc\n".$no_global_name); + my ($err, $cmds) = run_test(1, $debian_ctl, 'ok'); + is($err, undef, 'valid config passes'); + is_deeply($cmds, [ $ctl_std, $httpd_std ], + 'apachectl configtest runs as before'); + unlink($inc); + set_apache_conf($no_global_name); +}; + subtest 'old Apache versions are not tested' => sub { no warnings 'once'; local $main::httpd_modules{'core'} = 1.3;