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();