Fix to simply skip stale PID file check

https://github.com/webmin/webmin/pull/2775#issuecomment-4792905755
This commit is contained in:
Ilia Ross
2026-06-24 22:13:14 +02:00
parent 18bf94af6a
commit 18296d3a55
2 changed files with 4 additions and 125 deletions

View File

@@ -139,22 +139,15 @@ if (-l $perl_path) {
# Check vital config options
&update_vital_config();
# Check if already running via the PID file
if (open(PIDFILE, $config{'pidfile'})) {
# Check if already running via the PID file. In foreground mode, systemd or
# the socket bind owns duplicate-start detection.
if (!$config{'nofork'} && !$nofork_argv && open(PIDFILE, $config{'pidfile'})) {
my $already = <PIDFILE>;
close(PIDFILE);
chomp($already);
$already =~ s/^\s+|\s+$//g;
if ($already =~ /^\d+$/ && $already != $$ && kill(0, $already)) {
my $active = &pid_is_miniserv_for_config($already, $config_file);
if (defined($active) && !$active) {
warn "Ignoring stale Webmin PID file $config{'pidfile'} ".
"for unrelated PID $already\n";
unlink($config{'pidfile'});
}
else {
die "Webmin is already running with PID $already\n";
}
die "Webmin is already running with PID $already\n";
}
}
@@ -5191,56 +5184,6 @@ close($fh);
return $rv;
}
# read_process_cmdline(pid)
# Returns a process command line from /proc, or undef if unavailable.
sub read_process_cmdline
{
my ($pid) = @_;
return undef if (!$pid || $pid !~ /^\d+$/);
return &read_any_file("/proc/$pid/cmdline");
}
# same_config_file(file1, file2)
# Returns 1 if two config paths refer to the same file.
sub same_config_file
{
my ($file1, $file2) = @_;
return 0 if (!$file1 || !$file2);
return 1 if ($file1 eq $file2);
my @stat1 = stat($file1);
my @stat2 = stat($file2);
return @stat1 && @stat2 && $stat1[0] == $stat2[0] &&
$stat1[1] == $stat2[1];
}
# pid_is_miniserv_for_config(pid, config-file)
# Returns 1 if pid appears to be miniserv for this config, 0 if it is a
# different process, or undef if this platform cannot tell.
sub pid_is_miniserv_for_config
{
my ($pid, $config_file) = @_;
my $cmdline = &read_process_cmdline($pid);
return undef if (!defined($cmdline));
return 0 if ($cmdline eq "");
my @args = split(/\0/, $cmdline);
my $script_idx;
for(my $i=0; $i<@args; $i++) {
if ($args[$i] =~ /(?:^|\/)miniserv\.pl$/) {
$script_idx = $i;
last;
}
}
return 0 if (!defined($script_idx));
my $config_arg;
for(my $i=$script_idx+1; $i<@args; $i++) {
next if ($args[$i] =~ /^-/);
$config_arg = $args[$i];
last;
}
return defined($config_arg) ?
(&same_config_file($config_arg, $config_file) ? 1 : 0) : undef;
}
# update_vital_config()
# Updates %config with defaults, and dies if something vital is missing
sub update_vital_config

View File

@@ -1282,70 +1282,6 @@ subtest 'read_any_file' => sub {
'open failure returns undef');
};
# pid_is_miniserv_for_config — avoids stale pidfiles with reused PIDs
subtest 'pid_is_miniserv_for_config' => sub {
my $config = '/etc/webmin/miniserv.conf';
my $pid = $$;
{
no warnings 'redefine';
local *miniserv::read_process_cmdline = sub {
return join("\0", '/usr/sbin/dovecot', '-F');
};
is(miniserv::pid_is_miniserv_for_config($pid, $config), 0,
'live unrelated PID is not treated as active miniserv');
}
{
no warnings 'redefine';
local *miniserv::read_process_cmdline = sub {
return join("\0", '/usr/bin/perl',
'/usr/libexec/webmin/miniserv.pl',
'--nofork', $config);
};
is(miniserv::pid_is_miniserv_for_config($pid, $config), 1,
'matching miniserv config is treated as active');
}
{
require File::Temp;
my ($fh, $real_config) = File::Temp::tempfile(UNLINK => 1);
close($fh);
my $link_config = "$real_config.link";
SKIP: {
symlink($real_config, $link_config) ||
skip 'symlinks are unavailable', 1;
no warnings 'redefine';
local *miniserv::read_process_cmdline = sub {
return join("\0", '/usr/bin/perl',
'/usr/libexec/webmin/miniserv.pl',
$link_config);
};
is(miniserv::pid_is_miniserv_for_config($pid, $real_config), 1,
'symlinked config path is matched by inode');
unlink($link_config);
}
}
{
no warnings 'redefine';
local *miniserv::read_process_cmdline = sub {
return join("\0", '/usr/bin/perl',
'/usr/libexec/usermin/miniserv.pl',
'--nofork', '/etc/usermin/miniserv.conf');
};
is(miniserv::pid_is_miniserv_for_config($pid, $config), 0,
'different miniserv config is not treated as active');
}
{
no warnings 'redefine';
local *miniserv::read_process_cmdline = sub { return undef; };
is(miniserv::pid_is_miniserv_for_config($pid, $config), undef,
'unreadable process command line keeps conservative behavior');
}
};
# read_mime_types — populates %mime from file + addtype_* config
subtest 'read_mime_types' => sub {
no warnings 'once';