Merge branch 'master' of github.com:webmin/webmin

This commit is contained in:
Jamie Cameron
2026-08-17 21:26:37 -07:00
4 changed files with 100 additions and 32 deletions
+15 -7
View File
@@ -1,16 +1,24 @@
## Changelog ## Changelog
#### 2.654 (August, 2026) #### 2.660 (August 17, 2026)
* Add incremental ban time options to the Fail2Ban module
* Add Btrfs subvolume quota management to the Disk Quotas module, with full and simple accounting modes
* Add support for creating `vfsv1` Linux quota files for limits above 4 TiB, while preserving existing quota file formats * 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
* Add support for openSUSE 16 vendor and local Logrotate configuration overlays [#2682](https://github.com/webmin/webmin/issues/2682) * Add support for openSUSE 16 vendor and local Logrotate configuration overlays [#2682](https://github.com/webmin/webmin/issues/2682)
* Fix to ignore failures when adding IPv6 link-local (fe80::) addresses that may already be configured automatically * Add support for applying multiple patches at once with the `patch` sub-command
* Add APT package hold management to the Package Updates module
* Add incremental ban time options to the Fail2Ban module
* Fix Webmin server connections that open but never send a request, preventing them from waiting indefinitely [#2815](https://github.com/webmin/webmin/pull/2815)
* Fix to ignore failures when adding IPv6 link-local (fe80\:\:) addresses that may already be configured automatically
* Fixed creation of permissions new log files in the System Logs module (thanks to Kevin Carter) * Fixed creation of permissions new log files in the System Logs module (thanks to Kevin Carter)
* Fix Fail2Ban jail editor to correctly separate actions when one has no parameters [#2718](https://github.com/webmin/webmin/issues/2718)
* Update the Authentic theme to the latest version with various improvements: * Update the Authentic theme to the latest version with various improvements:
- Fix change detection and submission for forms using grouped bottom action buttons
- Fix login page front side clipping and flip animation for long welcome messages
- Fix opening the wrong directory in Terminal for domain owners in File Manager
- Fix disallowed entry handling in File Manager [forum.virtualmin.com/t/137654](https://forum.virtualmin.com/t/extra-admin-file-manager-permissions/137654?u=ilia) - Fix disallowed entry handling in File Manager [forum.virtualmin.com/t/137654](https://forum.virtualmin.com/t/extra-admin-file-manager-permissions/137654?u=ilia)
- Fix File Manager errors for Webmin-only users - Fix inline images not displaying when printing emails in Usermin
- Fix File Manager opening the wrong directory in Terminal for domain owners - Fix navigation detection for the new Virtualmin Podman plugin
- Fix support for navigation detection for the upcoming Virtualmin Podman plugin - Fix bottom page action buttons wrapping and alignment
- Fix errors for Webmin-only users in File Manager
#### 2.653 (July 26, 2026) #### 2.653 (July 26, 2026)
* Fix to include missing `xmlrpc-lib.pl` file in the package * Fix to include missing `xmlrpc-lib.pl` file in the package
+74 -21
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env perl #!/usr/bin/env perl
# patch - Apply a patch to Webmin core or its modules from GitHub or a local file # patch - Apply one or more patches to Webmin core or its modules from
# GitHub or a local file
use strict; use strict;
use warnings; use warnings;
@@ -49,20 +50,56 @@ if (!has_command('patch')) {
} }
} }
# Get patch URL or file # Get patch URLs or files
my $patch = $ARGV[0]; my @patches = @ARGV;
# Params check # Params check
if (!$patch) { if (!@patches) {
pod2usage(0); pod2usage(0);
exit 1; exit 1;
} }
# Apply all patches in the given order, stopping at the first failure
my $orig_cwd = cwd();
my $applied = 0;
my $failed = 0;
foreach my $patch (@patches) {
# Show which patch is being applied if there are multiple
print "Applying $patch ..\n" if (@patches > 1);
# Always start from the original directory, as applying a patch
# may change it
chdir($orig_cwd);
if (!apply_patch($patch)) {
$failed = 1;
last;
}
$applied++;
}
# Restart Webmin once, if any patches were applied, so that changes to
# any file, including miniserv.pl itself, take effect
restart_miniserv() if ($applied);
# Report if some patches were not applied
if ($failed) {
print "Stopped: $applied of ".scalar(@patches)." patches applied\n"
if (@patches > 1);
exit 1;
}
exit 0;
# apply_patch(patch)
# Apply a single patch given as URL or local file, printing the outcome
# and returning 1 on success or 0 on failure
sub apply_patch
{
my ($patch) = @_;
# Patch check # Patch check
if ($patch !~ /^https?:\/\//) { if ($patch !~ /^https?:\/\//) {
if (!-r $patch) { if (!-r $patch) {
print "Patch file $patch doesn't exist\n"; print "Patch file $patch doesn't exist\n";
exit 1; return 0;
} }
} }
elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ && elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ &&
@@ -101,9 +138,9 @@ if ($patch =~ m{
# Check if module exists # Check if module exists
if (!-d "$path/$module") { if (!-d "$path/$module") {
print "Module '$module' doesn't exist\n"; print "Module '$module' doesn't exist\n";
exit 1; return 0;
} }
# Prepare patch command # Prepare patch command
my $cmd; my $cmd;
@@ -128,7 +165,7 @@ if ($patch =~ m{^https?://raw\.githubusercontent\.com/} ||
} }
else { else {
print "Patch failed: Can't parse file name from URL\n"; print "Patch failed: Can't parse file name from URL\n";
exit 1; return 0;
} }
my $cd = "$path/$module/$dir"; my $cd = "$path/$module/$dir";
$cd =~ s|/+|/|g; $cd =~ s|/+|/|g;
@@ -149,26 +186,34 @@ else {
if ($direct) { if ($direct) {
$output = `$cmd 2>&1`; $output = `$cmd 2>&1`;
if ($output != 200) { if ($output != 200) {
print "Patch failed: Cannot download '$filename'. HTTP status code: $output\n"; print "Patch failed: Cannot download '$filename'. HTTP status code: $output\n";
exit 1; return 0;
} }
} }
# Apply patch using patch command # Apply patch using patch command
elsif (has_command('patch')) { elsif (has_command('patch')) {
$output = `$cmd 2>&1 | patch -p1 --verbose 2>&1`; # Do not leave .orig backups behind when hunks apply with fuzz or offset
$output = `$cmd 2>&1 | patch -p1 --verbose --no-backup-if-mismatch 2>&1`;
if ($output !~ /succeeded/i) { if ($output !~ /succeeded/i) {
print "Patch failed: $output\n"; print "Patch failed: $output\n";
exit 1; return 0;
} }
} }
# Apply patch using git command # Apply patch using git command
else { else {
$output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`; # If the current directory is inside some git repository (e.g. Webmin
# root tracked with git), git would treat patch paths as relative to
# that repository root and silently skip them, so stop repository
# discovery at the parent directory to always apply patch paths
# relative to the current directory
local $ENV{'GIT_CEILING_DIRECTORIES'} = dirname(cwd());
# Allow reduced context, similar to the default fuzz used by patch
$output = `$cmd 2>&1 | git apply --reject --verbose -C1 --whitespace=fix 2>&1`;
if ($output !~ /applied patch.*?cleanly/i) { if ($output !~ /applied patch.*?cleanly/i) {
print "Patch failed: $output\n"; print "Patch failed: $output\n";
exit 1; return 0;
} }
} }
# Print results # Print results
if ($direct) { if ($direct) {
@@ -183,11 +228,10 @@ if ($direct) {
else { else {
print "Patch applied successfully to:\n"; print "Patch applied successfully to:\n";
print " $1\n" while $output =~ /^(?|Applied patch\s+(\S+)|patching file\s+(\S+))/mg; print " $1\n" while $output =~ /^(?|Applied patch\s+(\S+)|patching file\s+(\S+))/mg;
}
return 1;
} }
# Reload Webmin
reload_miniserv();
=pod =pod
=head1 NAME =head1 NAME
@@ -199,9 +243,13 @@ patch
Apply a patch to Webmin core or its modules from GitHub/GitLab, a local Apply a patch to Webmin core or its modules from GitHub/GitLab, a local
file, or by downloading and replacing the entire file from a raw URL. file, or by downloading and replacing the entire file from a raw URL.
Multiple patches can be given at once and are applied in the given order,
stopping at the first failure. Webmin is restarted only once after all
patches are applied.
=head1 SYNOPSIS =head1 SYNOPSIS
webmin patch patch-url/file webmin patch patch-url/file [patch-url/file ...]
=head1 OPTIONS =head1 OPTIONS
@@ -229,6 +277,11 @@ Examples of usage:
- cd /usr/libexec/webmin/virtual-server/pro && - cd /usr/libexec/webmin/virtual-server/pro &&
webmin patch /root/virtualmin-pro/patches/patch-1.patch webmin patch /root/virtualmin-pro/patches/patch-1.patch
Apply multiple patches at once.
- webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0 \
https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d
=back =back
=head1 LICENSE AND COPYRIGHT =head1 LICENSE AND COPYRIGHT
+4 -2
View File
@@ -210,7 +210,7 @@ my @w;
my $v = $dir->{'value'}; my $v = $dir->{'value'};
$v =~ s/\n/ /g; $v =~ s/\n/ /g;
while($v =~ /\S/) { while($v =~ /\S/) {
if ($v =~ /^([^\[]+\[[^\]]+\])\s*(.*)/) { if ($v =~ /^\s*([^\s\[]+\[[^\]]*\])\s*(.*)/) {
push(@w, $1); push(@w, $1);
$v = $2; $v = $2;
} }
@@ -408,7 +408,9 @@ my @rv;
my @v = ref($dir->{'value'}) eq 'ARRAY' ? @{$dir->{'value'}} my @v = ref($dir->{'value'}) eq 'ARRAY' ? @{$dir->{'value'}}
: split(/\n/, $dir->{'value'}); : split(/\n/, $dir->{'value'});
push(@rv, $dir->{'name'}." = ".shift(@v)); push(@rv, $dir->{'name'}." = ".shift(@v));
push(@rv, map { " ".$_ } @v); # Continuation # Indent continuation lines to align with the first value
my $indent = " " x (length($dir->{'name'}) + 3);
push(@rv, map { $indent.$_ } @v);
return @rv; return @rv;
} }
+7 -2
View File
@@ -73,8 +73,13 @@ elsif ($in{'now'}) {
print $text{'force_doingone'},"\n"; print $text{'force_doingone'},"\n";
($ex, $out) = &rotate_log_now($log); ($ex, $out) = &rotate_log_now($log);
print "<pre>$out</pre>"; if ($out =~ /\S/) {
if ($?) { print "<pre>",&html_escape($out),"</pre>";
}
else {
print "<br>\n";
}
if ($ex) {
print $text{'force_failed'},"<br>\n"; print $text{'force_failed'},"<br>\n";
} }
else { else {