From 578a41769e7db006c93e66db9f43bd423b348454 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 18 May 2026 23:05:25 +0200 Subject: [PATCH] Add ability to show pending apply state after config changes --- nginx/lang/en | 1 + nginx/nginx-lib.pl | 67 +++++++++++++++++++++++++++++++++++++----- nginx/save_manual.cgi | 2 +- nginx/t/server-files.t | 43 +++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 9 deletions(-) diff --git a/nginx/lang/en b/nginx/lang/en index 0d3e5b021..16f73813b 100644 --- a/nginx/lang/en +++ b/nginx/lang/en @@ -32,6 +32,7 @@ index_stopdesc=Shut down all running Nginx webserver processes. index_start=Start Nginx Webserver index_startdesc=Start up the Nginx webserver using the current configuration. index_restart=Apply Nginx Configuration +index_apply_changes=Apply Changes index_restartdesc=Apply the current configuration by stopping and re-starting the Nginx webserver. index_delete=Delete Selected Server Blocks index_eglobal=You are not allowed to edit global settings diff --git a/nginx/nginx-lib.pl b/nginx/nginx-lib.pl index 88f01eb36..0a26d7600 100644 --- a/nginx/nginx-lib.pl +++ b/nginx/nginx-lib.pl @@ -11,8 +11,11 @@ eval "use WebminCore;"; our %access = &get_module_acl(); our ($get_config_cache, $get_config_parent_cache, %list_directives_cache, @list_modules_cache, @open_config_files); -our (%config, %text, %in, $module_root_directory); +our ($last_config_change_flag, $last_restart_time_flag); +our (%config, %text, %in, $module_root_directory, $module_var_directory); &set_nginx_config_defaults(); +$last_config_change_flag = $module_var_directory."/config-flag"; +$last_restart_time_flag = $module_var_directory."/restart-flag"; my @lock_all_config_files_cache; @@ -522,9 +525,11 @@ if ($parent->{'type'}) { sub flush_config_file_lines { my ($parent) = @_; -foreach my $f (&unique(@open_config_files)) { +my @files = &unique(@open_config_files); +foreach my $f (@files) { &flush_file_lines($f); } +&update_last_config_change() if (@files); @open_config_files = ( ); } @@ -1480,7 +1485,11 @@ return $? ? $out : undef; sub start_nginx { my $out = &backquote_logged("$config{'start_cmd'} 2>&1 &1 $apply" : $apply; + my $url = "restart.cgi?$args"; + push(@rv, &ui_link($url, $label)); } elsif ($access{'stop'}) { push(@rv, &ui_link("start.cgi?$args", $text{'index_start'})); @@ -1510,6 +1527,33 @@ elsif ($access{'stop'}) { return join("
\n", @rv); } +# update_last_config_change() +# Updates the flag file indicating when the config was changed +sub update_last_config_change +{ +&open_lock_tempfile(my $fh, ">$last_config_change_flag", 0, 1); +&close_tempfile($fh); +} + +# update_last_restart_time() +# Updates the flag file indicating when the config was applied +sub update_last_restart_time +{ +&open_lock_tempfile(my $fh, ">$last_restart_time_flag", 0, 1); +&close_tempfile($fh); +} + +# needs_config_restart() +# Returns 1 if a restart is needed after a config change +sub needs_config_restart +{ +my @cst = stat($last_config_change_flag); +my @rst = stat($last_restart_time_flag); +return 0 if (!@cst); +return 1 if (!@rst); +return $cst[9] > $rst[9] ? 1 : 0; +} + # this_url() # Returns the current module URL sub this_url @@ -1610,6 +1654,7 @@ if ($empty) { } &unlink_logged($file); } +&update_last_config_change(); return scalar(@servers); } @@ -1723,6 +1768,7 @@ if ($err) { &unlink_logged($link); return &text('enable_etest', "".&html_escape($err).""); } +&update_last_config_change(); return undef; } @@ -1756,6 +1802,7 @@ if ($err) { } return &text('enable_etest', "".&html_escape($err).""); } +&update_last_config_change(); return undef; } @@ -2037,7 +2084,8 @@ if ($config{'add_link'}) { my $link = $server->{'file'}; $link =~ s/^.*\///; $link = $config{'add_link'}."/".$link; - &symlink_logged($server->{'file'}, $link); + &update_last_config_change() + if (&symlink_logged($server->{'file'}, $link)); } } @@ -2049,6 +2097,7 @@ sub delete_server_link my ($server) = @_; if ($config{'add_link'}) { my $file = $server->{'file'}; + my $changed; my $short = $file; $short =~ s/^.*\///; opendir(LINKDIR, $config{'add_link'}); @@ -2056,10 +2105,11 @@ if ($config{'add_link'}) { if ($f ne "." && $f ne ".." && (&resolve_links($config{'add_link'}."/".$f) eq $file || $short eq $f)) { - &unlink_logged($config{'add_link'}."/".$f); + $changed++ if (&unlink_logged($config{'add_link'}."/".$f)); } } closedir(LINKDIR); + &update_last_config_change() if ($changed); } } @@ -2074,7 +2124,8 @@ foreach my $l (@$lref) { $count++ if ($l =~ /\S/); } if (!$count) { - &unlink_logged($server->{'file'}); + &update_last_config_change() + if (&unlink_logged($server->{'file'})); } } diff --git a/nginx/save_manual.cgi b/nginx/save_manual.cgi index 741efb5c5..1b2f469a2 100755 --- a/nginx/save_manual.cgi +++ b/nginx/save_manual.cgi @@ -43,6 +43,6 @@ else { &print_tempfile($fh, $in{'data'}); &close_tempfile($fh); } +&update_last_config_change(); &webmin_log("manual", undef, $in{'file'}); &redirect(""); - diff --git a/nginx/t/server-files.t b/nginx/t/server-files.t index 892b83cb1..ea2de046d 100644 --- a/nginx/t/server-files.t +++ b/nginx/t/server-files.t @@ -130,11 +130,17 @@ subtest 'sites-available files are manageable and ordered' => sub { }; subtest 'disable removes only the enabled symlink' => sub { + no warnings 'once'; + unlink($main::last_config_change_flag); + unlink($main::last_restart_time_flag); + { no warnings 'redefine'; local *main::test_config = sub { return undef; }; is(main::disable_server_file($alpha), undef, 'disable succeeds'); } + ok(main::needs_config_restart(), + 'disable marks config as needing apply'); ok(-f $alpha, 'disable leaves the sites-available file in place'); ok(!-e File::Spec->catfile($enabled, 'alpha.conf'), @@ -150,11 +156,17 @@ subtest 'disable removes only the enabled symlink' => sub { }; subtest 'enable creates a symlink without touching the source file' => sub { + no warnings 'once'; + unlink($main::last_config_change_flag); + unlink($main::last_restart_time_flag); + { no warnings 'redefine'; local *main::test_config = sub { return undef; }; is(main::enable_server_file($beta), undef, 'enable succeeds'); } + ok(main::needs_config_restart(), + 'enable marks config as needing apply'); my $link = File::Spec->catfile($enabled, 'beta.conf'); ok(-f $beta, 'enable leaves the sites-available file in place'); @@ -164,18 +176,28 @@ subtest 'enable creates a symlink without touching the source file' => sub { }; subtest 'legacy create/delete link helpers still manage symlinks' => sub { + no warnings 'once'; my $echo = File::Spec->catfile($available, 'echo.conf'); my $echo_link = File::Spec->catfile($enabled, 'echo.conf'); write_text($echo, server_conf('echo.example', "\troot /srv/echo;\n")); my $server = { 'file' => $echo }; + unlink($main::last_config_change_flag); + unlink($main::last_restart_time_flag); main::create_server_link($server); ok(-l $echo_link, 'create_server_link creates expected symlink'); is(readlink($echo_link), $echo, 'created symlink points to server file'); + ok(main::needs_config_restart(), + 'create_server_link marks config as needing apply'); + main::update_last_restart_time(); + my $old = time() - 10; + utime($old, $old, $main::last_restart_time_flag); main::delete_server_link($server); ok(!-e $echo_link, 'delete_server_link removes expected symlink'); ok(-f $echo, 'delete_server_link leaves server file in place'); + ok(main::needs_config_restart(), + 'delete_server_link marks config as needing apply'); }; subtest 'disabled server blocks can be deleted from available files' => sub { @@ -342,4 +364,25 @@ subtest 'root and proxy summaries are detected' => sub { 'named-location proxy URL uses HTTP default port'); }; +subtest 'config change apply flag tracks pending changes' => sub { + no warnings 'once'; + unlink($main::last_config_change_flag); + unlink($main::last_restart_time_flag); + + ok(!main::needs_config_restart(), + 'no apply needed when no change flag exists'); + main::update_last_config_change(); + ok(main::needs_config_restart(), + 'apply needed after config change'); + main::update_last_restart_time(); + ok(!main::needs_config_restart(), + 'apply not needed after config has been applied'); + + my $old = time() - 10; + utime($old, $old, $main::last_restart_time_flag); + main::update_last_config_change(); + ok(main::needs_config_restart(), + 'apply needed when config change is newer than last apply'); +}; + done_testing();