Compare commits

..

21 Commits

Author SHA1 Message Date
Ilia Ross
76efb4ee10 Fix not to show runlevels config option for systemd 2025-07-12 19:40:04 +03:00
Ilia Ross
ed42dd5822 Fix to always show description 2025-07-11 15:25:02 +03:00
Ilia Ross
9dc877910f Fix to always show summary arrow when opened 2025-07-06 02:27:13 +03:00
Ilia Ross
a65f9f5d6c Fix to respect config option and show no description 2025-07-05 18:57:23 +03:00
Ilia Ross
4d417ea4bf Fix to put lengthy descriptions to a nice details element 2025-07-05 16:38:55 +03:00
Ilia Ross
2e245a1035 Add a separate "Unit type" column 2025-07-05 16:03:47 +03:00
Ilia Ross
049542b7ed Add DropInPaths property 2025-07-05 15:43:26 +03:00
Ilia Ross
780cc982b7 Revert "Fix to show .service unit type"
72621c2929 (commitcomment-161382677)

This reverts commit 37f9ce4bb4.
2025-07-04 01:18:58 +03:00
Jamie Cameron
848422d256 Merge branch 'master' of github.com:webmin/webmin 2025-07-03 15:15:20 -07:00
Jamie Cameron
f1e96e3097 Re-open the debug and error logs if cleared periodically 2025-07-03 15:11:10 -07:00
Ilia Ross
90f4265389 Fix to use unit term for systemd systems 2025-07-03 13:28:55 +03:00
Ilia Ross
37f9ce4bb4 Fix to show .service unit type
72621c2929 (commitcomment-161316248)
2025-07-03 13:10:00 +03:00
Jamie Cameron
ceb3e583a3 Reduce duplication in list of unit types 2025-07-02 16:51:17 -07:00
Jamie Cameron
436d6f70bb Its cleaner for the API to always return a list 2025-07-02 16:48:39 -07:00
Jamie Cameron
72621c2929 No need to show the .service suffix 2025-07-02 16:44:21 -07:00
Jamie Cameron
af07c6c9d9 The notimeout flag is never set 2025-07-02 16:21:11 -07:00
Jamie Cameron
740f5b9d49 delete support for the webmin_notimeout param, which as far as I can tell is never used 2025-07-02 15:10:19 -07:00
Jamie Cameron
cb1368f07c The need for this hack is long gone 2025-07-02 14:54:49 -07:00
Ilia Ross
755325f9a5 Fix to enable log clearing similar to packaged version 2025-07-02 22:08:26 +03:00
Ilia Ross
dd914c7ecf Fix to partially revert a fix to enable log rotation for Webmin logs
This reverts commit 58580b7f4b.

58580b7f4b (r161290315)
2025-07-02 21:39:36 +03:00
Ilia Ross
489583708e Add support for clickable badge for insecure connection on login page 2025-07-02 16:37:48 +03:00
10 changed files with 139 additions and 142 deletions

View File

@@ -422,6 +422,10 @@ details.ui_hidden_table_start > summary::-webkit-details-marker {
color: #a1acc0;
}
details:not([open]).on-hover:not(:hover) summary::after {
visibility: hidden !important;
}
details.inline {
cursor: pointer;
padding: 0;
@@ -819,7 +823,20 @@ body > .mode > b[data-mode="server-manager"] > a > .ff-cloudmin {
filter: grayscale(1) contrast(1);
}
.not-secure {
color: #f00a38;
color: #c40000;
float: right;
cursor: help;
}
.inherit-color::after,
.inherit-color::before,
.inherit-color:focus::after,
.inherit-color:focus::before,
.inherit-color:active::after,
.inherit-color:active::before,
.inherit-color:hover::after,
.inherit-color:hover::before,
.inherit-color:focus,
.inherit-color:active,
.inherit-color:hover,
.inherit-color {
color: inherit !important;
}

View File

@@ -335,11 +335,13 @@ elsif ($init_mode eq "systemd" && $access{'bootup'}) {
&select_invert_link("d"),
&ui_link("edit_systemd.cgi?new=1", $text{'index_sadd'}) );
print &ui_links_row(\@links);
print &ui_columns_start([ "", $text{'index_uname'},
$text{'index_udesc'},
$text{'index_ucstatus'},
$text{'index_uboot'},
$text{'index_ustatus'}, ]);
print &ui_columns_start([ "", $text{'systemd_name'},
$config{'desc'} ? $text{'systemd_desc'} : (),
$text{'systemd_type'},
$text{'systemd_status'},
$text{'systemd_boot'},
$text{'index_ustatus'} ]);
my $units_piped = join('|', map { quotemeta } &get_systemd_unit_types());
foreach $u (&list_systemd_services()) {
if ($u->{'legacy'}) {
$l = "edit_action.cgi?0+".&urlize($u->{'name'});
@@ -347,12 +349,23 @@ elsif ($init_mode eq "systemd" && $access{'bootup'}) {
else {
$l = "edit_systemd.cgi?name=".&urlize($u->{'name'});
}
my $sname = $u->{'name'};
my ($type) = $sname =~ /\.([^.]+)$/;
if (defined($type) && $type =~ /^(?:$units_piped)$/) {
$sname =~ s/\.$type$//;
}
else {
$type = '';
}
my $title = ($u->{'boot'} == -1 ?
&html_escape($sname) :
&ui_link($l, &html_escape($sname)));
my $desc = $config{'desc'} ? &html_escape($u->{'desc'}) : undef;
print &ui_columns_row([
&ui_checkbox("d", $u->{'name'}, undef),
$u->{'boot'} == -1 ?
&html_escape($u->{'name'}) :
&ui_link($l, &html_escape($u->{'name'})),
&html_escape($u->{'desc'}),
$title,
$desc // (),
$type,
$u->{'fullstatus'} || "<i>$text{'index_unknown'}</i>",
$u->{'boot'} == 1 ?
&ui_text_color("$text{'yes'}", 'success') :

View File

@@ -207,7 +207,7 @@ unless full unit name is passed
sub action_unit
{
my ($unit) = @_;
my $units_piped = &get_systemd_unit_types('|');
my $units_piped = join('|', &get_systemd_unit_types());
$unit .= ".service"
if ($unit !~ /\.($units_piped)$/);
return $unit;
@@ -2128,7 +2128,7 @@ if (@list_systemd_services_cache && !$noinit) {
return @list_systemd_services_cache;
}
my $units_piped = &get_systemd_unit_types('|');
my $units_piped = join('|', &get_systemd_unit_types());
# Get all systemd unit names
my $out = &backquote_command("systemctl list-units --full --all -t service --no-legend");
@@ -2181,7 +2181,7 @@ while(@units) {
while(@args < 100 && @units) {
push(@args, shift(@units));
}
my $out = &backquote_command("systemctl show --property=Id,Description,UnitFileState,ActiveState,SubState,ExecStart,ExecStop,ExecReload,ExecMainPID,FragmentPath ".join(" ", @args)." 2>/dev/null");
my $out = &backquote_command("systemctl show --property=Id,Description,UnitFileState,ActiveState,SubState,ExecStart,ExecStop,ExecReload,ExecMainPID,FragmentPath,DropInPaths ".join(" ", @args)." 2>/dev/null");
my @lines = split(/\r?\n/, $out);
my $curr;
my @units;
@@ -2415,7 +2415,7 @@ my ($name) = @_;
&restart_systemd();
}
=head2 get_systemd_unit_types([return-as-string-separated])
=head2 get_systemd_unit_types()
Returns a list of all systemd unit types. Returns a string
instead if separator param is set.
@@ -2423,14 +2423,8 @@ instead if separator param is set.
=cut
sub get_systemd_unit_types
{
my ($str_separator) = @_;
my @systemd_types = ('target', 'service', 'socket', 'device',
'mount', 'automount', 'swap', 'path',
'timer', 'snapshot', 'slice', 'scope',
'busname');
return $str_separator ?
join($str_separator, @systemd_types) :
@systemd_types;
return ('target', 'service', 'socket', 'device', 'mount', 'automount',
'swap', 'path', 'timer', 'snapshot', 'slice', 'scope', 'busname');
}
=head2 is_systemd_service(name)
@@ -2441,7 +2435,7 @@ Returns 1 if some service is managed by systemd
sub is_systemd_service
{
my ($name) = @_;
my $units_piped = &get_systemd_unit_types('|');
my $units_piped = join('|', &get_systemd_unit_types());
foreach my $s (&list_systemd_services(1)) {
if (($s->{'name'} eq $name ||
$s->{'name'} =~
@@ -2467,22 +2461,10 @@ my $systemd_unit_dir2 = "/lib/systemd/system";
if ($name) {
foreach my $p ($systemd_local_conf, $systemd_unit_dir1,
$systemd_unit_dir2) {
if (-r "$p/$name.service" ||
-r "$p/$name" ||
-r "$p/$name.target" ||
-r "$p/$name.socket" ||
-r "$p/$name.device" ||
-r "$p/$name.mount" ||
-r "$p/$name.automount" ||
-r "$p/$name.swap" ||
-r "$p/$name.path" ||
-r "$p/$name.timer" ||
-r "$p/$name.snapshot" ||
-r "$p/$name.slice" ||
-r "$p/$name.scope" ||
-r "$p/$name.busname") {
return $p;
foreach my $t (&get_systemd_unit_types()) {
return $p if (-r "$p/$name.$t");
}
return $p if (-r "$p/$name");
}
}
# Always use /etc/systemd/system for locally created units
@@ -2919,4 +2901,12 @@ my ($name) = @_;
return $name =~ /\./ ? $name : "com.webmin.".$name;
}
# config_pre_load(mod-info, [mod-order])
# Check if some config options are conditional
sub config_pre_load
{
my ($modconf_info, $modconf_order) = @_;
$modconf_info->{'desc'} =~ s/2-[^,]+,// if ($init_mode eq "systemd");
}
1;

View File

@@ -221,29 +221,30 @@ upstart_eserver2=Server command does not exist
upstart_eserver3=Only one server command can be entered
upstart_return=upstart service
systemd_title1=Create Systemd Service
systemd_title2=Edit Systemd Service
systemd_egone=Service no longer exists!
systemd_elegacy=Not a systemd service!
systemd_header=Systemd service details
systemd_name=Service name
systemd_title1=Create Systemd Unit
systemd_title2=Edit Systemd Unit
systemd_egone=Unit no longer exists!
systemd_elegacy=Not a systemd unit!
systemd_header=Systemd unit details
systemd_name=Unit name
systemd_type=Unit type
systemd_file=Configuration file
systemd_desc=Service description
systemd_desc=Unit description
systemd_start=Commands to run on startup
systemd_stop=Commands to run on shutdown
systemd_conf=Systemd configuration
systemd_conf=Systemd unit configuration
systemd_boot=Start at boot time?
systemd_status=Current status
systemd_status0=Not running
systemd_status1=Running with PID $1
systemd_status2=Running
systemd_status3=Unknown!
systemd_err=Failed to save systemd service
systemd_ename=Missing or invalid-lookup systemd service name
systemd_eclash=A service with the same name already exists
systemd_edesc=Missing service description
systemd_return=systemd service
systemd_econf=No systemd configuration entered
systemd_err=Failed to save systemd unit
systemd_ename=Missing or invalid-lookup systemd unit name
systemd_eclash=A unit with the same name already exists
systemd_edesc=Missing unit description
systemd_return=systemd unit
systemd_econf=No systemd unit configuration entered
systemd_estart=Missing commands to run on startup
launchd_title1=Create Launchd Agent

View File

@@ -190,7 +190,7 @@ pam_login=Continue
pam_restart=Restart
login_notsecure=Not Secure
login_notsecure_desc=This connection is not secure and could let a man-in-the-middle attack intercept your password or session cookie. Use HTTPS unless you are on a trusted local network or behind a secure reverse proxy.
login_notsecure_desc=This connection is not secure and could let a man-in-the-middle attack intercept your password or session cookie. Click this badge to switch to an HTTPS connection, unless you are on a trusted local network or behind a secure reverse proxy.
acl_root=Root directory for file chooser
acl_otherdirs=Other visible directories in file chooser

View File

@@ -383,12 +383,7 @@ foreach $mod (split(/\s+/, $config{'preuse'})) {
}
# Open debug log if set
if ($config{'debuglog'}) {
open(DEBUG, ">>$config{'debuglog'}");
chmod(0700, $config{'debuglog'});
select(DEBUG); $| = 1; select(STDOUT);
print DEBUG "miniserv.pl starting ..\n";
}
&open_debug_to_log("miniserv.pl starting ..\n");
# Write out (empty) blocked hosts file
&write_blocked_file();
@@ -595,9 +590,16 @@ if ($config{'logclear'}) {
# need to clear log
$write_logtime = 1;
unlink($config{'logfile'});
unlink($config{'errorlog'})
if ($config{'errorlog'} &&
$config{'errorlog'} ne '-');
unlink($config{'debuglog'})
if ($config{'debuglog'});
}
}
else { $write_logtime = 1; }
else {
$write_logtime = 1;
}
if ($write_logtime) {
open(LOGTIME, ">$config{'logfile'}.time");
print LOGTIME time(),"\n";
@@ -610,53 +612,6 @@ if ($config{'logclear'}) {
push(@childpids, $logclearer);
}
# session_state(session-id, set-state)
# Sets or gets the active state of the client, which is used to determine
# whether the client is away or not. This is used to allow for the session to
# expire after a period of inactivity in case a client has opened connections or
# makes HTTP requests in the background. Returns 1 if the client is currently
# not away (ie. active), or 0 if it is away.
sub session_state
{
my ($sid, $set) = @_;
return 1 if (!$sid);
# Check session database
my %sessiondb;
dbmopen(%sessiondb, $config{'sessiondb'}, 0700);
if ($@) {
dbmclose(%sessiondb);
eval "use NDBM_File";
dbmopen(%sessiondb, $config{'sessiondb'}, 0700);
}
# Get current record
my $skey = &hash_session_id($sid);
my ($user, $ltime, $ip, $lifetime, $active);
if (exists($sessiondb{$skey})) {
($user, $ltime, $ip, $lifetime, $active) =
split(/\s+/, $sessiondb{$skey});
$lifetime //= 0; # preserve or default to 0
$active //= 1; # default to 'alive'
}
# Update flag if caller supplied a value
if ($user && $ltime && $ip && defined($set)) {
$active = $set ? 1 : 0;
$sessiondb{$skey} = join(' ', $user, $ltime, $ip, $lifetime, $active);
print DEBUG "websocket updated status for $sid to $active\n";
}
else {
print DEBUG "websocket current status for $sid is $active\n";
}
# Save the record back to the database
dbmclose(%sessiondb);
# Return the active state
return $active;
}
# Setup the logout time dbm if needed
if ($config{'session'}) {
eval "use SDBM_File";
@@ -681,6 +636,16 @@ local $remove_session_count = 0;
$need_pipes = $config{'passdelay'} || $config{'session'};
$cron_runs = 0;
while(1) {
# Periodically re-open error and debug logs if deleted via regular
# log clearing
if ($config{'errorlog'} && $config{'errorlog'} ne '-' &&
!-e $config{'errorlog'}) {
&redirect_stderr_to_log();
}
if ($config{'debuglog'} && !-e $config{'debuglog'}) {
&open_debug_to_log();
}
# Check if any webmin cron jobs are ready to run
&execute_ready_webmin_crons($cron_runs++);
@@ -1151,11 +1116,10 @@ while(1) {
}
$userlast{$1} = $time_now;
}
elsif ($inline =~ /^verify\s+(\S+)\s+(\S+)\s+(\S+)/) {
elsif ($inline =~ /^verify\s+(\S+)\s+(\S+)/) {
# Verifying a session ID
local $session_id = $1;
local $notimeout = $2;
local $vip = $3;
local $vip = $2;
local $skey = $sessiondb{$session_id} ?
$session_id :
&hash_session_id($session_id);
@@ -1164,14 +1128,11 @@ while(1) {
print $outfd "0 0\n";
}
else {
local ($user, $ltime, $ip, $lifetime, $state) =
local ($user, $ltime, $ip, $lifetime) =
split(/\s+/, $sessiondb{$skey});
$lifetime //= 0;
$state //= 1;
local $lot = &get_logout_time($user, $session_id);
if ($lot &&
$time_now - $ltime > $lot*60 &&
!$notimeout) {
$time_now - $ltime > $lot*60) {
# Session has timed out due to
# idle time being hit
print $outfd "1 ",($time_now - $ltime),"\n";
@@ -1196,7 +1157,7 @@ while(1) {
# Session is OK, update last time
# and remote IP
print $outfd "2 $user\n";
$sessiondb{$skey} = "$user $time_now $vip $lifetime $state";
$sessiondb{$skey} = "$user $time_now $vip";
}
}
}
@@ -1900,7 +1861,7 @@ if ($config{'session'} && !$deny_authentication &&
&http_error(500, "Invalid session",
"Session ID contains invalid characters");
}
print $PASSINw "verify $sid 0 $acptip\n";
print $PASSINw "verify $sid $acptip\n";
<$PASSOUTr> =~ /^(\d+)\s+(\S+)/;
if ($1 != 2) {
&http_error(500, "Invalid session",
@@ -2071,9 +2032,7 @@ if ($config{'session'} && !$validated) {
local $cookie = $header{'cookie'};
while($cookie =~ s/(^|\s|;)$sidname=([a-f0-9]+)//) {
$session_id = $2;
local $notimeout =
$in{'webmin_notimeout'} ? 1 : 0;
print $PASSINw "verify $session_id $notimeout $acptip\n";
print $PASSINw "verify $session_id $acptip\n";
<$PASSOUTr> =~ /(\d+)\s+(\S+)/;
if ($1 == 2) {
# Valid session continuation
@@ -5590,14 +5549,7 @@ foreach my $pe (split(/\t+/, $config{'expires_paths'})) {
}
# Re-open debug log
close(DEBUG);
if ($config{'debuglog'}) {
open(DEBUG, ">>$config{'debuglog'}");
select(DEBUG); $| = 1; select(STDOUT);
}
else {
open(DEBUG, ">/dev/null");
}
&open_debug_to_log();
# Reset cache of sudo checks
undef(%sudocache);
@@ -5981,11 +5933,10 @@ while(1) {
syswrite($fh, $buf, length($buf)) || last;
}
my $now = time();
if ($now - $last_session_check_time > 10 &&
&session_state($session_id) == 1) {
if ($now - $last_session_check_time > 10) {
# Re-validate the browser session every 10 seconds
print DEBUG "verifying websockets session $session_id\n";
print $PASSINw "verify $session_id 0 $acptip\n";
print $PASSINw "verify $session_id $acptip\n";
<$PASSOUTr> =~ /(\d+)\s+(\S+)/;
if ($1 != 2) {
print DEBUG "session $session_id has expired!\n";
@@ -6747,6 +6698,7 @@ else {
sub redirect_stderr_to_log
{
if ($config{'errorlog'} ne '-') {
close(STDERR);
open(STDERR, ">>$config{'errorlog'}") ||
die "failed to open $config{'errorlog'} : $!";
if ($config{'logperms'}) {
@@ -6756,6 +6708,23 @@ if ($config{'errorlog'} ne '-') {
select(STDERR); $| = 1; select(STDOUT);
}
# open_debug_to_log([msg])
# Direct the DEBUG file handle somewhere
sub open_debug_to_log
{
my ($msg) = @_;
close(DEBUG);
if ($config{'debuglog'}) {
open(DEBUG, ">>$config{'debuglog'}");
chmod(0700, $config{'debuglog'});
select(DEBUG); $| = 1; select(STDOUT);
print DEBUG $msg if ($msg);
}
else {
open(DEBUG, ">/dev/null");
}
}
# should_gzip_file(filename)
# Returns 1 if some path should be gzipped
sub should_gzip_file

View File

@@ -74,7 +74,13 @@ print &ui_hidden("cid", $in{'cid'});
my $not_secure;
if ($ENV{'HTTPS'} ne 'ON') {
$not_secure = ui_tag('span', "&#9888; $text{'login_notsecure'}",
my $link = ui_tag('a', "&#9888; $text{'login_notsecure'}",
{ 'href' => "javascript:void(0);",
'class' => 'inherit-color',
'onclick' => "window.location.href = ".
"window.location.href.replace(/^http:/, 'https:'); return false;",
});
$not_secure = ui_tag('span', $link,
{ class => 'not-secure', title => $text{'login_notsecure_desc'} });
}

View File

@@ -94,7 +94,13 @@ print &ui_hidden("page", $in{'page'});
my $not_secure;
if ($ENV{'HTTPS'} ne 'ON') {
$not_secure = ui_tag('span', "&#9888; $text{'login_notsecure'}",
my $link = ui_tag('a', "&#9888; $text{'login_notsecure'}",
{ 'href' => "javascript:void(0);",
'class' => 'inherit-color',
'onclick' => "window.location.href = ".
"window.location.href.replace(/^http:/, 'https:'); return false;",
});
$not_secure = ui_tag('span', $link,
{ class => 'not-secure', title => $text{'login_notsecure_desc'} });
}

View File

@@ -406,6 +406,7 @@ else {
'errorlog' => "$var_dir/miniserv.error",
'pidfile' => "$var_dir/miniserv.pid",
'logtime' => 168,
'logclear' => 1,
'ppath' => $ppath,
'ssl' => $ssl,
'no_ssl2' => 1,
@@ -778,12 +779,13 @@ else {
}
system("$perl ".&quote_path("$wadir/copyconfig.pl")." ".&quote_path("$os_type/$real_os_type")." ".&quote_path("$os_version/$real_os_version")." ".&quote_path($wadir)." ".$config_directory." \"\" ".$allmods . " >/dev/null 2>&1");
if (!$upgrading) {
# Store the OS and version
# Store the OS and version, and enable log and log clearing
&read_file("$config_directory/config", \%gconfig);
$gconfig{'os_type'} = $os_type;
$gconfig{'os_version'} = $os_version;
$gconfig{'real_os_type'} = $real_os_type;
$gconfig{'real_os_version'} = $real_os_version;
$gconfig{'logclear'} = 1;
$gconfig{'log'} = 1;
&write_file("$config_directory/config", \%gconfig);
}

View File

@@ -829,13 +829,6 @@ if [ "$upgrading" != 1 ]; then
# Disallow unknown referers by default
echo "referers_none=1" >>$config_dir/config
else
# Enable log rotation if not set
grep logclear= $config_dir/miniserv.conf >/dev/null
if [ "$?" != "0" ]; then
echo "logclear=1" >> $config_dir/miniserv.conf
echo "logclear=1" >> $config_dir/config
fi
# Enable HSTS by default if not set
grep ssl_hsts= $config_dir/miniserv.conf >/dev/null
if [ "$?" != "0" ]; then