mirror of
https://github.com/webmin/webmin.git
synced 2026-08-23 07:20:28 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d16d9ea205 | ||
|
|
f7da47cd5e | ||
|
|
0660a18276 | ||
|
|
6183a4006a | ||
|
|
850cc1f498 | ||
|
|
523853963e | ||
|
|
3c26b2be3b |
@@ -1,4 +1,9 @@
|
||||
## Changelog
|
||||
#### 2.661 (September, 2026)
|
||||
* Add options to send Webmin and Usermin errors to the systemd journal [forum.virtualmin.com/t/136562](https://forum.virtualmin.com/t/miniserv-webserver-log-growing-too-big-should-be-rotated/136562)
|
||||
* Add webserver logging controls to Usermin Configuration module
|
||||
* Add option to rotate Webmin and Usermin webserver logs using `logrotate` instead of periodically clearing them [#2821](https://github.com/webmin/webmin/pull/2821)
|
||||
|
||||
#### 2.660 (August 20, 2026)
|
||||
* Add support for creating `vfsv1` Linux quota files for limits above 4 TiB, while preserving existing quota file formats
|
||||
* Add Btrfs subvolume quota management to the Disk Quotas module, with full and simple accounting modes
|
||||
|
||||
@@ -168,6 +168,31 @@ return $file if (!$local);
|
||||
return ©_vendor_config($file, $local);
|
||||
}
|
||||
|
||||
# ensure_writable_config_file(file)
|
||||
# Makes a config file safe to edit, by creating the local override that
|
||||
# shadows it when it belongs to the vendor tree. Callers must use this before
|
||||
# saving directives, as save_directive refuses to write to a vendor file.
|
||||
sub ensure_writable_config_file
|
||||
{
|
||||
my ($file) = @_;
|
||||
if (&is_vendor_main_config($file)) {
|
||||
return &ensure_local_main_config();
|
||||
}
|
||||
elsif (&is_vendor_config_file($file)) {
|
||||
return &ensure_local_config_override($file);
|
||||
}
|
||||
elsif (my $vendor = &get_vendor_config_file($file)) {
|
||||
# A local path that shadows a vendor file, but does not exist yet
|
||||
return &ensure_local_config_override($vendor);
|
||||
}
|
||||
elsif ($file eq &get_config_parent()->{'file'} &&
|
||||
&is_vendor_main_config(&get_main_config_file())) {
|
||||
# New directives go to the main config, which is still the vendor one
|
||||
return &ensure_local_main_config();
|
||||
}
|
||||
return $file;
|
||||
}
|
||||
|
||||
# list_config_dir_files(directory, [relative-subdirectory])
|
||||
# Returns relative and absolute paths for regular files below a directory
|
||||
sub list_config_dir_files
|
||||
|
||||
@@ -43,6 +43,6 @@ $o->{'bootup'} = $in{'bootup'};
|
||||
|
||||
sub get_icons
|
||||
{
|
||||
return ( "access" ,"bind" ,"ui" ,"umods" ,"os" ,"lang" ,"upgrade" ,"session" ,"assignment" ,"categories" ,"themes", "referers", "anon", "ssl" ,"configs" ,"acl" ,"restrict" ,"users" ,"defacl", "sessions", "blocked", "advanced" );
|
||||
return ( "access" ,"bind" ,"log" ,"ui" ,"umods" ,"os" ,"lang" ,"upgrade" ,"session" ,"assignment" ,"categories" ,"themes", "referers", "anon", "ssl" ,"configs" ,"acl" ,"restrict" ,"users" ,"defacl", "sessions", "blocked", "advanced" );
|
||||
}
|
||||
|
||||
|
||||
65
usermin/change_log.cgi
Executable file
65
usermin/change_log.cgi
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Save Usermin webserver logging options
|
||||
|
||||
require './usermin-lib.pl';
|
||||
&ReadParse();
|
||||
$access{'log'} || &error($text{'acl_ecannot'});
|
||||
&error_setup($text{'log_err'});
|
||||
&get_usermin_miniserv_config(\%miniserv);
|
||||
|
||||
# Only a change of error destination on a systemd-managed service needs the
|
||||
# drop-in and a full restart; otherwise the option is ignored.
|
||||
my $journal_changed = defined($in{'error_journal'}) &&
|
||||
&webmin::miniserv_systemd_journal_available("usermin.service") &&
|
||||
($miniserv{'errorlog'} eq '-' ? 1 : 0) != ($in{'error_journal'} ? 1 : 0);
|
||||
|
||||
# Either Miniserv clears the logs itself, or logrotate takes them over. There
|
||||
# is no setting for the latter, so it is on when clearing is off and a section
|
||||
# already rotates the logs.
|
||||
my $logrotate = $in{'logclear'} == 2 ? 1 : 0;
|
||||
!$logrotate || &webmin::miniserv_logrotate_available() ||
|
||||
&error($text{'log_elogrotate'});
|
||||
my $was_logrotate = !int($miniserv{'logclear'}) &&
|
||||
&webmin::miniserv_logrotate_available() &&
|
||||
&webmin::get_miniserv_logrotate_section(\%miniserv) ? 1 : 0;
|
||||
|
||||
# Validate and save the access-log settings.
|
||||
$miniserv{'log'} = $in{'log'};
|
||||
$miniserv{'loghost'} = $in{'loghost'};
|
||||
$miniserv{'logtrust'} = $in{'logtrust'};
|
||||
$miniserv{'logclf'} = $in{'logclf'};
|
||||
$miniserv{'logclear'} = $logrotate ? 0 : $in{'logclear'};
|
||||
!$miniserv{'logclear'} || $in{'logtime'} =~ /^[1-9][0-9]*$/ ||
|
||||
&error(&text('log_ehours', $in{'logtime'}));
|
||||
$miniserv{'logtime'} = $in{'logtime'};
|
||||
|
||||
# Save the error destination and matching systemd drop-in when supported.
|
||||
&lock_file($usermin_miniserv_config);
|
||||
if ($journal_changed) {
|
||||
&webmin::set_miniserv_error_destination(\%miniserv,
|
||||
$usermin_miniserv_config, "usermin.service",
|
||||
$in{'error_journal'});
|
||||
}
|
||||
else {
|
||||
&put_usermin_miniserv_config(\%miniserv);
|
||||
}
|
||||
&unlock_file($usermin_miniserv_config);
|
||||
|
||||
# Hand the Miniserv logs to logrotate, or take them back from it
|
||||
if ($logrotate) {
|
||||
&webmin::setup_miniserv_logrotate(\%miniserv, "usermin");
|
||||
}
|
||||
elsif ($was_logrotate) {
|
||||
&webmin::remove_miniserv_logrotate(\%miniserv);
|
||||
}
|
||||
|
||||
# Restart through systemd when stderr needs to be re-attached.
|
||||
if ($journal_changed) {
|
||||
&webmin::restart_miniserv_systemd_service("usermin.service", 2);
|
||||
}
|
||||
else {
|
||||
&restart_usermin_miniserv();
|
||||
}
|
||||
|
||||
&webmin_log("log");
|
||||
&redirect("");
|
||||
@@ -29,3 +29,4 @@ advanced=1
|
||||
mobile=1
|
||||
twofactor=1
|
||||
web=1
|
||||
log=1
|
||||
|
||||
46
usermin/edit_log.cgi
Executable file
46
usermin/edit_log.cgi
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/local/bin/perl
|
||||
# Display Usermin webserver logging options
|
||||
|
||||
require './usermin-lib.pl';
|
||||
$access{'log'} || &error($text{'acl_ecannot'});
|
||||
&ui_print_header(undef, $text{'log_title'}, "");
|
||||
&get_usermin_miniserv_config(\%miniserv);
|
||||
|
||||
print &text('log_desc', "<tt>$miniserv{'logfile'}</tt>"),"<p>\n";
|
||||
print &ui_form_start("change_log.cgi", "post");
|
||||
print &ui_table_start($text{'log_header'}, undef, 2);
|
||||
|
||||
# Control the Usermin access log and its built-in expiry mechanism.
|
||||
print &ui_table_row($text{'log_status'},
|
||||
&ui_radio("log", $miniserv{'log'} ? 1 : 0,
|
||||
[ [ 1, $text{'log_enable'} ],
|
||||
[ 0, $text{'log_disable'} ] ]));
|
||||
print &ui_table_row($text{'log_resolv'},
|
||||
&ui_yesno_radio("loghost", int($miniserv{'loghost'})));
|
||||
print &ui_table_row($text{'log_trust'},
|
||||
&ui_yesno_radio("logtrust", int($miniserv{'logtrust'})));
|
||||
print &ui_table_row($text{'log_clf'},
|
||||
&ui_yesno_radio("logclf", int($miniserv{'logclf'})));
|
||||
my @clear_opts = ( [ 1, &text('log_period',
|
||||
&ui_textbox("logtime", $miniserv{'logtime'}, 10)) ] );
|
||||
my $logclear = int($miniserv{'logclear'});
|
||||
if (&webmin::miniserv_logrotate_available()) {
|
||||
push(@clear_opts, [ 2, $text{'log_logrotate'} ]);
|
||||
$logclear = 2 if (!$logclear &&
|
||||
&webmin::get_miniserv_logrotate_section(\%miniserv));
|
||||
}
|
||||
push(@clear_opts, [ 0, $text{'no'} ]);
|
||||
print &ui_table_row($text{'log_clear'},
|
||||
&ui_radio("logclear", $logclear, \@clear_opts));
|
||||
|
||||
# Only systemd services can safely inherit stderr into the journal.
|
||||
if (&webmin::miniserv_systemd_journal_available("usermin.service")) {
|
||||
print &ui_table_row($text{'log_error'},
|
||||
&ui_radio("error_journal", $miniserv{'errorlog'} eq '-' ? 1 : 0,
|
||||
[ [ 0, $text{'log_error_file'} ],
|
||||
[ 1, $text{'log_error_journal'} ] ]));
|
||||
}
|
||||
|
||||
print &ui_table_end();
|
||||
print &ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
BIN
usermin/images/log.gif
Normal file
BIN
usermin/images/log.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 470 B |
@@ -55,6 +55,7 @@ if (!$miniserv{'root'} && !-d $miniserv{'root'}) {
|
||||
|
||||
@links = ( "edit_access.cgi",
|
||||
"edit_bind.cgi",
|
||||
"edit_log.cgi",
|
||||
"edit_ui.cgi",
|
||||
"edit_mods.cgi",
|
||||
"edit_os.cgi",
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
index_title=Usermin Configuration
|
||||
log_title=Logging
|
||||
log_desc=Usermin can be configured to write a log of web server hits, in the standard CLF log file format. If logging is enabled, you can also choose whether IP addresses or hostnames are recorded, and how often the log file is cleared. When enabled, logs are written to the file $1.
|
||||
log_header=Webserver logging options
|
||||
log_status=Logging active?
|
||||
log_enable=Enable logging
|
||||
log_disable=Disable logging
|
||||
log_resolv=Log resolved hostnames?
|
||||
log_trust=Log client IP address when behind proxy?
|
||||
log_clf=Use combined log format (including referrer and user agent)?
|
||||
log_clear=Periodically clear log files?
|
||||
log_period=Yes, every $1 hours
|
||||
log_logrotate=Yes, rotate using <tt>logrotate</tt>
|
||||
log_elogrotate=The <tt>logrotate</tt> configuration is not available on this system
|
||||
log_error=Error log destination
|
||||
log_error_file=Write to <tt>miniserv.error</tt>
|
||||
log_error_journal=Write to the <tt>systemd journal</tt>
|
||||
log_err=Failed to save logging options
|
||||
log_ehours='$1' is not a valid number of hours
|
||||
index_econfig=The directory $1 either does not exist on your system, or is not the Usermin configuration directory. Maybe Usermin is not installed, or your <a href='$2'>module configuration</a> is incorrect.
|
||||
index_return=Usermin configuration
|
||||
index_stop=Stop Usermin
|
||||
|
||||
@@ -4859,11 +4859,25 @@ return if ($nocache == 2);
|
||||
if (!$system_hostname[$m]) {
|
||||
if ($gconfig{'os_type'} ne 'windows') {
|
||||
# If systemd system try /etc/hostname straight away
|
||||
my $initsys = &trim(&backquote_command("cat /proc/1/comm 2>/dev/null"));
|
||||
if ($initsys eq 'systemd') {
|
||||
if (-d "/run/systemd/system") {
|
||||
my $hostname = &read_file_contents("/etc/hostname");
|
||||
$hostname =~ s/\r|\n//g;
|
||||
# Cache the short name before resolving the full name
|
||||
if ($hostname) {
|
||||
my $shortname = $hostname;
|
||||
$shortname =~ s/\..*$//;
|
||||
$system_hostname[1] = $shortname;
|
||||
}
|
||||
# Resolve a short static hostname before caching it
|
||||
if ($hostname && !$m && $hostname !~ /\./ &&
|
||||
!$gconfig{'no_hostname_f'}) {
|
||||
my $fqdn;
|
||||
my $ex = &execute_command("hostname -f", undef,
|
||||
\$fqdn, undef, 0, 1);
|
||||
$fqdn = &trim($fqdn);
|
||||
$hostname = $fqdn if (!$ex && $fqdn =~ /\./);
|
||||
}
|
||||
if ($hostname && ($m || $hostname =~ /\./)) {
|
||||
$hostname =~ s/\..*$// if ($m);
|
||||
$system_hostname[$m] = $hostname;
|
||||
return $hostname;
|
||||
@@ -7607,6 +7621,14 @@ my ($param_action,
|
||||
|
||||
my $m = $param_module ? $param_module : &get_module_name();
|
||||
|
||||
# When logrotate has emptied the actions log, throw away the file changes
|
||||
# and annotations that belonged to the rotated entries
|
||||
if ($gconfig{'logrotate'} && -e $webmin_logfile && !-s _) {
|
||||
&unlink_file("$ENV{'WEBMIN_VAR'}/diffs");
|
||||
&unlink_file("$ENV{'WEBMIN_VAR'}/files");
|
||||
&unlink_file("$ENV{'WEBMIN_VAR'}/annotations");
|
||||
}
|
||||
|
||||
if ($gconfig{'logclear'}) {
|
||||
# check if it is time to clear the log
|
||||
my @st = stat("$webmin_logfile.time");
|
||||
|
||||
@@ -9,12 +9,29 @@ require './webmin-lib.pl';
|
||||
&lock_file($ENV{'MINISERV_CONFIG'});
|
||||
&get_miniserv_config(\%miniserv);
|
||||
my ($miniserv_log, $in_log) = ($miniserv{'log'}, $in{'log'});
|
||||
|
||||
# Only a change of error destination on a systemd-managed service needs the
|
||||
# drop-in and a full restart; otherwise the option is ignored.
|
||||
my $journal_changed = defined($in{'error_journal'}) &&
|
||||
&miniserv_systemd_journal_available("webmin.service") &&
|
||||
($miniserv{'errorlog'} eq '-' ? 1 : 0) != ($in{'error_journal'} ? 1 : 0);
|
||||
|
||||
$miniserv{'log'} = $in{'log'};
|
||||
$miniserv{'loghost'} = $in{'loghost'};
|
||||
$miniserv{'logtrust'} = $in{'logtrust'};
|
||||
$miniserv{'logclf'} = $in{'logclf'};
|
||||
$miniserv{'logclear'} = $in{'logclear'};
|
||||
!$in{'logclear'} || $in{'logtime'} =~ /^[1-9][0-9]*$/ ||
|
||||
|
||||
# Either Miniserv clears the logs itself, or logrotate takes them over. There
|
||||
# is no setting for the latter, so it is on when clearing is off and a section
|
||||
# already rotates the logs.
|
||||
my $logrotate = $in{'logclear'} == 2 ? 1 : 0;
|
||||
!$logrotate || &miniserv_logrotate_available() ||
|
||||
&error($text{'log_elogrotate'});
|
||||
my $was_logrotate = !int($miniserv{'logclear'}) &&
|
||||
&miniserv_logrotate_available() &&
|
||||
&get_miniserv_logrotate_section(\%miniserv) ? 1 : 0;
|
||||
$miniserv{'logclear'} = $logrotate ? 0 : $in{'logclear'};
|
||||
!$miniserv{'logclear'} || $in{'logtime'} =~ /^[1-9][0-9]*$/ ||
|
||||
&error(&text('log_ehours', $in{'logtime'}));
|
||||
$miniserv{'logtime'} = $in{'logtime'};
|
||||
if ($in{'perms_def'}) {
|
||||
@@ -42,12 +59,29 @@ if (defined($in{'login'})) {
|
||||
delete($miniserv{'failed_script'});
|
||||
}
|
||||
}
|
||||
&put_miniserv_config(\%miniserv);
|
||||
# Save the error destination and matching systemd drop-in when supported.
|
||||
if ($journal_changed) {
|
||||
&set_miniserv_error_destination(\%miniserv,
|
||||
$ENV{'MINISERV_CONFIG'}, "webmin.service",
|
||||
$in{'error_journal'});
|
||||
}
|
||||
else {
|
||||
&put_miniserv_config(\%miniserv);
|
||||
}
|
||||
&unlock_file($ENV{'MINISERV_CONFIG'});
|
||||
|
||||
# Hand the Miniserv and actions logs to logrotate, or take them back from it
|
||||
if ($logrotate) {
|
||||
&setup_miniserv_logrotate(\%miniserv, "webmin", [ $webmin_logfile ]);
|
||||
}
|
||||
elsif ($was_logrotate) {
|
||||
&remove_miniserv_logrotate(\%miniserv, [ $webmin_logfile ]);
|
||||
}
|
||||
|
||||
$gconfig{'log'} = $in{'log'};
|
||||
$gconfig{'logtime'} = $in{'logtime'};
|
||||
$gconfig{'logclear'} = $in{'logclear'};
|
||||
$gconfig{'logclear'} = $miniserv{'logclear'};
|
||||
$gconfig{'logrotate'} = $logrotate;
|
||||
$gconfig{'logusers'} =
|
||||
$in{'uall'} ? '' : join(" ", split(/\0/, $in{'users'}));
|
||||
$gconfig{'logmodules'} =
|
||||
@@ -71,6 +105,12 @@ if ($miniserv_log != $in_log) {
|
||||
}
|
||||
}
|
||||
|
||||
&show_restart_page();
|
||||
# Restart through systemd when stderr needs to be re-attached.
|
||||
if ($journal_changed) {
|
||||
&restart_miniserv_systemd_service("webmin.service", 2);
|
||||
&redirect("");
|
||||
}
|
||||
else {
|
||||
&show_restart_page();
|
||||
}
|
||||
&webmin_log("log", undef, undef, \%in);
|
||||
|
||||
|
||||
@@ -31,12 +31,26 @@ print &ui_table_row($text{'log_trust'},
|
||||
print &ui_table_row($text{'log_clf'},
|
||||
&ui_yesno_radio("logclf", int($miniserv{'logclf'})));
|
||||
|
||||
# Clear logs regularly
|
||||
# Clear logs regularly, or hand them over to logrotate when available
|
||||
my @clear_opts = ( [ 1, &text('log_period',
|
||||
&ui_textbox("logtime", $miniserv{'logtime'}, 10)) ] );
|
||||
my $logclear = int($miniserv{'logclear'});
|
||||
if (&miniserv_logrotate_available()) {
|
||||
push(@clear_opts, [ 2, $text{'log_logrotate'} ]);
|
||||
$logclear = 2 if (!$logclear &&
|
||||
&get_miniserv_logrotate_section(\%miniserv));
|
||||
}
|
||||
push(@clear_opts, [ 0, $text{'no'} ]);
|
||||
print &ui_table_row($text{'log_clear2'},
|
||||
&ui_radio("logclear", int($miniserv{'logclear'}),
|
||||
[ [ 1, &text('log_period',
|
||||
&ui_textbox("logtime", $miniserv{'logtime'}, 10)) ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
&ui_radio("logclear", $logclear, \@clear_opts));
|
||||
|
||||
# A systemd service can keep Miniserv errors in the journal instead.
|
||||
if (&miniserv_systemd_journal_available("webmin.service")) {
|
||||
print &ui_table_row($text{'log_error'},
|
||||
&ui_radio("error_journal", $miniserv{'errorlog'} eq '-' ? 1 : 0,
|
||||
[ [ 0, $text{'log_error_file'} ],
|
||||
[ 1, $text{'log_error_journal'} ] ]));
|
||||
}
|
||||
|
||||
print &ui_table_hr();
|
||||
|
||||
@@ -101,4 +115,3 @@ print &ui_table_end();
|
||||
print &ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
|
||||
@@ -93,6 +93,11 @@ log_trust=Log client IP address when behind proxy?
|
||||
log_clf=Use combined log format (including referrer and user agent)?
|
||||
log_clear2=Periodically clear log files?
|
||||
log_period=Yes, every $1 hours
|
||||
log_logrotate=Yes, rotate using <tt>logrotate</tt>
|
||||
log_elogrotate=The <tt>logrotate</tt> configuration is not available on this system
|
||||
log_error=Error log destination
|
||||
log_error_file=Write to <tt>miniserv.error</tt>
|
||||
log_error_journal=Write to the <tt>systemd journal</tt>
|
||||
log_forusers=Users to log Webmin actions for
|
||||
log_uall=Log actions by all users
|
||||
log_users=Only log actions by ..
|
||||
|
||||
@@ -43,6 +43,211 @@ if (!-r $update_cache) {
|
||||
|
||||
our $primary_host = "webmin.com";
|
||||
our $primary_ssl = $can_http_ssl;
|
||||
|
||||
=head2 miniserv_systemd_journal_available(unit)
|
||||
|
||||
Returns 1 if a systemd service is available for receiving Miniserv errors.
|
||||
|
||||
=cut
|
||||
sub miniserv_systemd_journal_available
|
||||
{
|
||||
my ($unit) = @_;
|
||||
return &has_command("systemctl") &&
|
||||
(-r "/etc/systemd/system/$unit" ||
|
||||
-r "/usr/lib/systemd/system/$unit" ||
|
||||
-r "/lib/systemd/system/$unit");
|
||||
}
|
||||
|
||||
=head2 set_miniserv_error_destination(&miniserv, config, unit, journal)
|
||||
|
||||
Selects the Miniserv error file or the systemd journal, using a local systemd
|
||||
drop-in so that the packaged service unit remains unchanged.
|
||||
|
||||
=cut
|
||||
sub set_miniserv_error_destination
|
||||
{
|
||||
my ($miniserv, $config_file, $unit, $journal) = @_;
|
||||
$unit =~ /^(webmin|usermin)\.service$/ || return 0;
|
||||
my $dropin_dir = "/etc/systemd/system/$unit.d";
|
||||
my $dropin = "$dropin_dir/10-miniserv-error-log.conf";
|
||||
|
||||
# The dash keeps Miniserv's stderr attached to the service manager.
|
||||
if ($journal) {
|
||||
$miniserv->{'errorlog'} = '-';
|
||||
&make_dir($dropin_dir, 0755) if (!-d $dropin_dir);
|
||||
&write_file_contents($dropin,
|
||||
"[Service]\nStandardError=journal\n");
|
||||
}
|
||||
else {
|
||||
my $logfile = $miniserv->{'logfile'};
|
||||
$logfile =~ s![^/]+$!miniserv.error!;
|
||||
$miniserv->{'errorlog'} = $logfile;
|
||||
&unlink_file($dropin) if (-e $dropin);
|
||||
# Take the directory too, unless other drop-ins are still using it
|
||||
rmdir($dropin_dir) if (-d $dropin_dir);
|
||||
}
|
||||
|
||||
# Apply the Miniserv and service-manager settings as one UI operation.
|
||||
&write_file($config_file, $miniserv);
|
||||
&system_logged("systemctl daemon-reload >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
=head2 restart_miniserv_systemd_service(unit, [delay])
|
||||
|
||||
Fully restarts a Miniserv systemd service so that stderr is re-attached to the
|
||||
selected destination. A delay allows the current Webmin response to finish.
|
||||
|
||||
=cut
|
||||
sub restart_miniserv_systemd_service
|
||||
{
|
||||
my ($unit, $delay) = @_;
|
||||
$unit =~ /^(webmin|usermin)\.service$/ || return 0;
|
||||
$delay = int($delay);
|
||||
if ($delay) {
|
||||
&system_logged("(sleep $delay; systemctl --no-block restart $unit) " .
|
||||
">/dev/null 2>&1 </dev/null &");
|
||||
}
|
||||
else {
|
||||
&system_logged("systemctl restart $unit >/dev/null 2>&1 </dev/null");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 miniserv_logrotate_available()
|
||||
|
||||
Returns 1 if logrotate is installed and can be configured from Webmin.
|
||||
|
||||
=cut
|
||||
sub miniserv_logrotate_available
|
||||
{
|
||||
return &foreign_available("logrotate") && &foreign_installed("logrotate")
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
=head2 miniserv_log_files(&miniserv)
|
||||
|
||||
Returns the Miniserv access and error log paths that rotation applies to.
|
||||
|
||||
=cut
|
||||
sub miniserv_log_files
|
||||
{
|
||||
my ($miniserv) = @_;
|
||||
my $errorlog = $miniserv->{'logfile'};
|
||||
$errorlog =~ s![^/]+$!miniserv.error!;
|
||||
$errorlog = $miniserv->{'errorlog'} if ($miniserv->{'errorlog'} =~ /^\//);
|
||||
return &unique($miniserv->{'logfile'}, $errorlog);
|
||||
}
|
||||
|
||||
=head2 get_miniserv_logrotate_section(&miniserv)
|
||||
|
||||
Returns the logrotate section that covers the Miniserv access log, if any.
|
||||
|
||||
=cut
|
||||
sub get_miniserv_logrotate_section
|
||||
{
|
||||
my ($miniserv) = @_;
|
||||
&foreign_require("logrotate");
|
||||
my ($logfile) = &miniserv_log_files($miniserv);
|
||||
# Global directives like weekly and rotate share the list with log sections,
|
||||
# but their name is a plain string instead of a list of log files
|
||||
foreach my $c (@{&logrotate::get_config()}) {
|
||||
next if (ref($c->{'name'}) ne 'ARRAY');
|
||||
return $c if (&indexof($logfile, @{$c->{'name'}}) >= 0);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
=head2 setup_miniserv_logrotate(&miniserv, name, [&extra-logs])
|
||||
|
||||
Adds any missing Miniserv and extra logs to the access-log section, or creates
|
||||
a section when none exists. Copy-truncate rotation lets Miniserv keep its open
|
||||
error log handle, as it only re-opens that log when the file disappears.
|
||||
|
||||
=cut
|
||||
sub setup_miniserv_logrotate
|
||||
{
|
||||
my ($miniserv, $name, $extra) = @_;
|
||||
my @logs = &unique(&miniserv_log_files($miniserv), @{$extra || [ ]});
|
||||
my $lconf = &get_miniserv_logrotate_section($miniserv);
|
||||
|
||||
# Skip any log that some other section already rotates
|
||||
my %covered;
|
||||
foreach my $c (@{&logrotate::get_config()}) {
|
||||
next if (ref($c->{'name'}) ne 'ARRAY');
|
||||
foreach my $log (@{$c->{'name'}}) {
|
||||
$covered{$log} = 1;
|
||||
}
|
||||
}
|
||||
my @missing = grep { !$covered{$_} } @logs;
|
||||
return 0 if ($lconf && !@missing);
|
||||
|
||||
# Vendor files cannot be edited in place, so copy first on systems that
|
||||
# keep their logrotate config under /usr
|
||||
my $file = $lconf ? $lconf->{'file'} : &logrotate::get_add_file($name);
|
||||
&logrotate::ensure_writable_config_file($file);
|
||||
my $parent = &logrotate::get_config_parent();
|
||||
if ($lconf) {
|
||||
# Copying re-reads the config, so look the section up again
|
||||
$lconf = &get_miniserv_logrotate_section($miniserv);
|
||||
push(@{$lconf->{'name'}}, @missing);
|
||||
}
|
||||
else {
|
||||
$lconf = { 'file' => &logrotate::get_add_file($name),
|
||||
'name' => [ grep { !$covered{$_} } @logs ],
|
||||
'members' => [ { 'name' => 'weekly' },
|
||||
{ 'name' => 'rotate', 'value' => 7 },
|
||||
{ 'name' => 'missingok' },
|
||||
{ 'name' => 'notifempty' },
|
||||
{ 'name' => 'compress' },
|
||||
{ 'name' => 'copytruncate' } ] };
|
||||
}
|
||||
&lock_file($lconf->{'file'});
|
||||
&logrotate::save_directive($parent,
|
||||
defined($lconf->{'index'}) ? $lconf : undef,
|
||||
$lconf);
|
||||
&flush_file_lines($lconf->{'file'});
|
||||
&unlock_file($lconf->{'file'});
|
||||
&logrotate::flush_logrotate_config_cache();
|
||||
return 1;
|
||||
}
|
||||
|
||||
=head2 remove_miniserv_logrotate(&miniserv, [&extra-logs])
|
||||
|
||||
Removes the Miniserv logs and any extra logs from their logrotate section. The
|
||||
section and its file are deleted when they cover nothing else.
|
||||
|
||||
=cut
|
||||
sub remove_miniserv_logrotate
|
||||
{
|
||||
my ($miniserv, $extra) = @_;
|
||||
my $lconf = &get_miniserv_logrotate_section($miniserv);
|
||||
return 0 if (!$lconf);
|
||||
&logrotate::ensure_writable_config_file($lconf->{'file'});
|
||||
|
||||
# Copying re-reads the config, so look the section up again
|
||||
$lconf = &get_miniserv_logrotate_section($miniserv);
|
||||
my %logs = map { $_, 1 } (&miniserv_log_files($miniserv), @{$extra || [ ]});
|
||||
my @leftover = grep { !$logs{$_} } @{$lconf->{'name'}};
|
||||
my $parent = &logrotate::get_config_parent();
|
||||
&lock_file($lconf->{'file'});
|
||||
if (@leftover) {
|
||||
# Other logs share the section, so only drop ours
|
||||
$lconf->{'name'} = \@leftover;
|
||||
&logrotate::save_directive($parent, $lconf, $lconf);
|
||||
&flush_file_lines($lconf->{'file'});
|
||||
&unlock_file($lconf->{'file'});
|
||||
&logrotate::flush_logrotate_config_cache();
|
||||
}
|
||||
else {
|
||||
&logrotate::save_directive($parent, $lconf, undef);
|
||||
&flush_file_lines($lconf->{'file'});
|
||||
&unlock_file($lconf->{'file'});
|
||||
&logrotate::flush_logrotate_config_cache();
|
||||
&logrotate::delete_if_empty($lconf->{'file'});
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
our $primary_port = $primary_ssl ? 443 : 80;
|
||||
|
||||
our $webmin_key_email = "jcameron\@webmin.com";
|
||||
|
||||
Reference in New Issue
Block a user