Add option to send Miniserv errors to the systemd journal
Add a Logging page to the Usermin Configuration module for the access-log settings, guarded by a new "log" ACL. Also, Webmin and Usermin logging pages add new an "Error log destination" choice on systemd systems. Selecting the journal sets errorlog=- and installs a local StandardError=journal drop-in, leaving the packaged unit untouched. https://forum.virtualmin.com/t/miniserv-webserver-log-growing-too-big-should-be-rotated/136562/10?u=ilia
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
## 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
|
||||
|
||||
#### 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
|
||||
|
||||
@@ -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" );
|
||||
}
|
||||
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/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 needs the systemd drop-in and restart.
|
||||
my $journal_changed = defined($in{'error_journal'}) &&
|
||||
($miniserv{'errorlog'} eq '-' ? 1 : 0) != ($in{'error_journal'} ? 1 : 0);
|
||||
|
||||
# Validate and save the access-log settings.
|
||||
!$in{'logclear'} || $in{'logtime'} =~ /^[1-9][0-9]*$/ ||
|
||||
&error(&text('log_ehours', $in{'logtime'}));
|
||||
$miniserv{'log'} = $in{'log'};
|
||||
$miniserv{'loghost'} = $in{'loghost'};
|
||||
$miniserv{'logtrust'} = $in{'logtrust'};
|
||||
$miniserv{'logclf'} = $in{'logclf'};
|
||||
$miniserv{'logclear'} = $in{'logclear'};
|
||||
$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);
|
||||
|
||||
# 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
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/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'})));
|
||||
print &ui_table_row($text{'log_clear'},
|
||||
&ui_radio("logclear", int($miniserv{'logclear'}),
|
||||
[ [ 1, &text('log_period',
|
||||
&ui_textbox("logtime", $miniserv{'logtime'}, 10)) ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
|
||||
# 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'});
|
||||
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,20 @@
|
||||
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_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
|
||||
|
||||
+22
-2
@@ -9,6 +9,11 @@ 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 needs the systemd drop-in and restart.
|
||||
my $journal_changed = defined($in{'error_journal'}) &&
|
||||
($miniserv{'errorlog'} eq '-' ? 1 : 0) != ($in{'error_journal'} ? 1 : 0);
|
||||
|
||||
$miniserv{'log'} = $in{'log'};
|
||||
$miniserv{'loghost'} = $in{'loghost'};
|
||||
$miniserv{'logtrust'} = $in{'logtrust'};
|
||||
@@ -42,7 +47,15 @@ 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'});
|
||||
|
||||
$gconfig{'log'} = $in{'log'};
|
||||
@@ -71,6 +84,13 @@ 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);
|
||||
|
||||
|
||||
+8
-1
@@ -38,6 +38,14 @@ print &ui_table_row($text{'log_clear2'},
|
||||
&ui_textbox("logtime", $miniserv{'logtime'}, 10)) ],
|
||||
[ 0, $text{'no'} ] ]));
|
||||
|
||||
# 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();
|
||||
|
||||
# Webmin users to log for
|
||||
@@ -101,4 +109,3 @@ print &ui_table_end();
|
||||
print &ui_form_end([ [ "save", $text{'save'} ] ]);
|
||||
|
||||
&ui_print_footer("", $text{'index_return'});
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ 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_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,73 @@ 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);
|
||||
}
|
||||
|
||||
# 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;
|
||||
}
|
||||
our $primary_port = $primary_ssl ? 443 : 80;
|
||||
|
||||
our $webmin_key_email = "jcameron\@webmin.com";
|
||||
|
||||
Reference in New Issue
Block a user