mirror of
https://github.com/webmin/webmin.git
synced 2026-02-03 14:13:29 +00:00
Compare commits
134 Commits
dev/impove
...
dev/patch-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7507433bf1 | ||
|
|
248cb719c0 | ||
|
|
94b7fdf0ec | ||
|
|
d89f6411b6 | ||
|
|
01d08a3605 | ||
|
|
3d9497ff45 | ||
|
|
a6832450d1 | ||
|
|
e41037388c | ||
|
|
a7b3af534b | ||
|
|
87e006ceeb | ||
|
|
fbee8f0588 | ||
|
|
e077b4da94 | ||
|
|
38efad8265 | ||
|
|
4d2a1fa084 | ||
|
|
13b2eca3b1 | ||
|
|
aa4c3b1de6 | ||
|
|
edaab4fd6f | ||
|
|
6fded0862c | ||
|
|
4006b0454e | ||
|
|
5d4ab58baa | ||
|
|
e6c7a60fe6 | ||
|
|
a75781d61a | ||
|
|
45348f5b02 | ||
|
|
548c078813 | ||
|
|
76ccb2b7ed | ||
|
|
61d2081371 | ||
|
|
458916b4d8 | ||
|
|
37451ad905 | ||
|
|
9eec58a1f1 | ||
|
|
178f527afa | ||
|
|
cd1555b3e1 | ||
|
|
2d7cfca67f | ||
|
|
e92a35b3ed | ||
|
|
e35efd0f00 | ||
|
|
a12f385a5b | ||
|
|
5dc7cfafd7 | ||
|
|
35aee74311 | ||
|
|
d04dfdf157 | ||
|
|
41f1adf0df | ||
|
|
1ae01bed8d | ||
|
|
4a6f5d9a6a | ||
|
|
bb3a4bc1e8 | ||
|
|
084c117547 | ||
|
|
0221a092b9 | ||
|
|
535d4173b3 | ||
|
|
20d481e96b | ||
|
|
0256ee47f2 | ||
|
|
6af5ed6e2f | ||
|
|
abeff44b1a | ||
|
|
35298efd8a | ||
|
|
3fa687e716 | ||
|
|
43fc057484 | ||
|
|
70e9a1c00b | ||
|
|
d21188c2ad | ||
|
|
a780103e2f | ||
|
|
4014293760 | ||
|
|
ba3be31335 | ||
|
|
adb3d0bb67 | ||
|
|
cd9bc8b7c4 | ||
|
|
e1ebcf0506 | ||
|
|
17a27dbe00 | ||
|
|
e36e943251 | ||
|
|
95ee1e2f2d | ||
|
|
37cde80bbe | ||
|
|
45852664fe | ||
|
|
00885b1f76 | ||
|
|
cce8911f0f | ||
|
|
3a151469c7 | ||
|
|
e3b94dc458 | ||
|
|
cde548ada3 | ||
|
|
00d0a89630 | ||
|
|
9c971202b3 | ||
|
|
2aa3bce4da | ||
|
|
4103177d1e | ||
|
|
31b6cdc82b | ||
|
|
fd8c634277 | ||
|
|
596ba13b1e | ||
|
|
5e684bf41b | ||
|
|
356c8f7f53 | ||
|
|
185465351a | ||
|
|
8d84e7313a | ||
|
|
71e37adfed | ||
|
|
af912d9539 | ||
|
|
5b31c7df84 | ||
|
|
55b5939194 | ||
|
|
00ddfd4d05 | ||
|
|
2d23a3503e | ||
|
|
a838d11a26 | ||
|
|
5f28a28d8d | ||
|
|
e13df24539 | ||
|
|
4f7924338d | ||
|
|
3a1d609579 | ||
|
|
e441427031 | ||
|
|
469857a41e | ||
|
|
e47c82e7e8 | ||
|
|
a0f6dd935c | ||
|
|
e302b706ec | ||
|
|
8c7fc88d51 | ||
|
|
7b4d905eb6 | ||
|
|
a1a6f669b2 | ||
|
|
0298d884ef | ||
|
|
5a8b3467a1 | ||
|
|
17fb8304c3 | ||
|
|
5cd88dad43 | ||
|
|
c15e7a5e5e | ||
|
|
fad464be47 | ||
|
|
489db4c769 | ||
|
|
cc663af3df | ||
|
|
0b58cd5197 | ||
|
|
dbd16c21cc | ||
|
|
8ddabb35b6 | ||
|
|
8476206da8 | ||
|
|
ca3362ee84 | ||
|
|
e88ba87eae | ||
|
|
a420c7142f | ||
|
|
6f37dc94bf | ||
|
|
c59a200725 | ||
|
|
e56aa7711c | ||
|
|
b480b4caa3 | ||
|
|
db456ad458 | ||
|
|
9513d85157 | ||
|
|
dccc3fb10e | ||
|
|
bb7938a0f5 | ||
|
|
8164480b48 | ||
|
|
4155fdb4c5 | ||
|
|
19efd89c28 | ||
|
|
f911137624 | ||
|
|
d4ac34e4b5 | ||
|
|
5323bda372 | ||
|
|
1b1ac686e3 | ||
|
|
75e9323429 | ||
|
|
554b439bf8 | ||
|
|
2f9a0b3f21 | ||
|
|
cc2502737f |
File diff suppressed because one or more lines are too long
153
bin/patch
Executable file
153
bin/patch
Executable file
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
# patch - Apply a patch to Webmin core or its modules from GitHub or a local file
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use 5.010;
|
||||||
|
|
||||||
|
use Getopt::Long qw(:config permute pass_through);
|
||||||
|
use Pod::Usage;
|
||||||
|
use File::Basename;
|
||||||
|
use Cwd qw(cwd);
|
||||||
|
|
||||||
|
my %opt;
|
||||||
|
GetOptions(
|
||||||
|
'help|h' => \$opt{'help'},
|
||||||
|
'config|c=s' => \$opt{'config'},
|
||||||
|
);
|
||||||
|
pod2usage(0) if ($opt{'help'});
|
||||||
|
|
||||||
|
# Get Webmin path
|
||||||
|
my $path = cwd;
|
||||||
|
my $lib = "web-lib-funcs.pl";
|
||||||
|
if (!-r "$path/$lib") {
|
||||||
|
$path = dirname(dirname($0));
|
||||||
|
if (!-r "$path/$lib") {
|
||||||
|
$path = $path = Cwd::realpath('..');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Init core
|
||||||
|
my $config_dir = $opt{'config'} || '/etc/webmin';
|
||||||
|
$ENV{'WEBMIN_CONFIG'} = $config_dir;
|
||||||
|
push(@INC, $path);
|
||||||
|
eval 'use WebminCore';
|
||||||
|
init_config();
|
||||||
|
|
||||||
|
# Check if curl is installed
|
||||||
|
if (!has_command('curl')) {
|
||||||
|
print "curl is not installed\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if git is installed
|
||||||
|
if (!has_command('git')) {
|
||||||
|
print "git is not installed\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get patch URL or file
|
||||||
|
my $patch = $ARGV[0];
|
||||||
|
|
||||||
|
# Params check
|
||||||
|
if (!$patch) {
|
||||||
|
pod2usage(0);
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Patch check
|
||||||
|
if ($patch !~ /^https?:\/\//) {
|
||||||
|
if (!-r $patch) {
|
||||||
|
print "Patch file $patch doesn't exist\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ &&
|
||||||
|
$patch !~ /\.patch$/ && $patch !~ /\.diff$/) {
|
||||||
|
$patch .= '.patch';
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse module name from URL
|
||||||
|
my $module = "";
|
||||||
|
if ($patch =~ m{https://(github|gitlab)\.com/[^/]+/([^/]+)/commit/[^/]+}) {
|
||||||
|
$module = $2;
|
||||||
|
$module = "" if ($2 eq 'webmin');
|
||||||
|
# Special handling for some modules
|
||||||
|
$module = $module =~ /^virtualmin-pro$/ ?
|
||||||
|
'virtual-server/pro' :
|
||||||
|
'virtual-server'
|
||||||
|
if $module =~ /^virtualmin-(gpl|pro)$/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if module exists
|
||||||
|
if (!-d "$path/$module") {
|
||||||
|
print "Module $module doesn't exist\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download command or cat patch file
|
||||||
|
my $cmd;
|
||||||
|
if ($patch =~ /^https?:\/\//) {
|
||||||
|
$cmd = "curl -s @{[quotemeta($patch)]}";
|
||||||
|
chdir "$path/$module";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cmd = "cat @{[quotemeta($patch)]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply patch using Git
|
||||||
|
my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`;
|
||||||
|
if ($output !~ /applied patch.*?cleanly/i) {
|
||||||
|
print "Patch failed: $output\n";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
print "Patch applied successfully to:\n";
|
||||||
|
print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg;
|
||||||
|
system("$config_dir/restart");
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
patch
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Apply a patch to Webmin core or its modules from GitHub or a local file.
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
webmin patch patch-url/file
|
||||||
|
|
||||||
|
=head1 OPTIONS
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item --help, -h
|
||||||
|
|
||||||
|
Give this help list.
|
||||||
|
|
||||||
|
=item --config, -c
|
||||||
|
|
||||||
|
Specify the full path to the Webmin configuration directory. Defaults to
|
||||||
|
C</etc/webmin>
|
||||||
|
|
||||||
|
Examples of usage:
|
||||||
|
|
||||||
|
Apply a patch from a URL.
|
||||||
|
|
||||||
|
- webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0.patch
|
||||||
|
|
||||||
|
- webmin patch https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d
|
||||||
|
|
||||||
|
Apply a patch from local file.
|
||||||
|
|
||||||
|
- cd /usr/libexec/webmin/virtual-server/pro &&
|
||||||
|
webmin patch /root/virtualmin-pro/patches/patch-1.patch
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright 2024 Ilia Ross <ilia@virtualmin.com>
|
||||||
@@ -1988,6 +1988,8 @@ if (@transfer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (@notify) {
|
if (@notify) {
|
||||||
|
my %done;
|
||||||
|
@notify = grep { !$done{$_->{'name'}}++ } @notify;
|
||||||
my $also = { 'name' => 'also-notify',
|
my $also = { 'name' => 'also-notify',
|
||||||
'type' => 1,
|
'type' => 1,
|
||||||
'members' => \@notify};
|
'members' => \@notify};
|
||||||
@@ -1996,6 +1998,8 @@ if (@notify) {
|
|||||||
'values' => [ 'yes' ] });
|
'values' => [ 'yes' ] });
|
||||||
}
|
}
|
||||||
if (@transfer) {
|
if (@transfer) {
|
||||||
|
my %done;
|
||||||
|
@transfer = grep { !$done{$_->{'name'}}++ } @transfer;
|
||||||
my $allow = { 'name' => 'allow-transfer',
|
my $allow = { 'name' => 'allow-transfer',
|
||||||
'type' => 1,
|
'type' => 1,
|
||||||
'members' => \@transfer };
|
'members' => \@transfer };
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ if (@keyrecs) {
|
|||||||
my $kt = $key->{'ksk'} ? 'ksk' : 'zone';
|
my $kt = $key->{'ksk'} ? 'ksk' : 'zone';
|
||||||
my ($keyrec) = grep { $_->{'values'}->[0] ==
|
my ($keyrec) = grep { $_->{'values'}->[0] ==
|
||||||
($key->{'ksk'} ? 257 : 256) } @keyrecs;
|
($key->{'ksk'} ? 257 : 256) } @keyrecs;
|
||||||
|
next if (!$keyrec);
|
||||||
my $keyline = format_dnssec_public_key(
|
my $keyline = format_dnssec_public_key(
|
||||||
join(" ", $keyrec->{'name'}, $keyrec->{'class'},
|
join(" ", $keyrec->{'name'}, $keyrec->{'class'},
|
||||||
$keyrec->{'type'}, @{$keyrec->{'values'}}));
|
$keyrec->{'type'}, @{$keyrec->{'values'}}));
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ if ($in{'source'} == 0) {
|
|||||||
if (!$in{'local'})
|
if (!$in{'local'})
|
||||||
{ &install_error($text{'download_elocal'}); }
|
{ &install_error($text{'download_elocal'}); }
|
||||||
if (!-r $in{'local'})
|
if (!-r $in{'local'})
|
||||||
{ &install_error(&text('download_elocal2', $in{'local'})); }
|
{ &install_error(&text('download_elocal2', &html_escape($in{'local'}))); }
|
||||||
$source = $in{'local'};
|
$source = $in{'local'};
|
||||||
@pfile = ( $in{'local'} );
|
@pfile = ( $in{'local'} );
|
||||||
$need_unlink = 0;
|
$need_unlink = 0;
|
||||||
@@ -91,8 +91,9 @@ elsif ($in{'source'} == 3) {
|
|||||||
$i = 0;
|
$i = 0;
|
||||||
@fallback = ( );
|
@fallback = ( );
|
||||||
foreach $yum (@cpanyum) {
|
foreach $yum (@cpanyum) {
|
||||||
print &text('download_yum', "<tt>$cpan[$i]</tt>",
|
print &text('download_yum',
|
||||||
"<tt>$yum->{'package'}</tt>"),"<br>\n";
|
"<tt>".&html_escape($cpan[$i])."</tt>",
|
||||||
|
"<tt>".&html_escape($yum->{'package'})."</tt>"),"<br>\n";
|
||||||
print "<ul>\n";
|
print "<ul>\n";
|
||||||
@got = &software::update_system_install(
|
@got = &software::update_system_install(
|
||||||
$yum->{'package'});
|
$yum->{'package'});
|
||||||
@@ -154,7 +155,8 @@ elsif ($in{'source'} == 3) {
|
|||||||
|
|
||||||
# Fail if any modules are missing from CPAN
|
# Fail if any modules are missing from CPAN
|
||||||
for($i=0; $i<@cpan; $i++) {
|
for($i=0; $i<@cpan; $i++) {
|
||||||
push(@missing, "<tt>$cpan[$i]</tt>") if (!$source[$i]);
|
push(@missing, "<tt>".&html_escape($cpan[$i])."</tt>")
|
||||||
|
if (!$source[$i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($in{'missingok'}) {
|
if ($in{'missingok'}) {
|
||||||
@@ -167,11 +169,12 @@ elsif ($in{'source'} == 3) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@cpan || &install_error(&text('download_ecpan',
|
@cpan || &install_error(&text('download_ecpan',
|
||||||
join(" ", @missing)));
|
&html_escape(join(" ", @missing))));
|
||||||
}
|
}
|
||||||
elsif (@missing) {
|
elsif (@missing) {
|
||||||
# Fail due to missing modules
|
# Fail due to missing modules
|
||||||
&install_error(&text('download_ecpan', join(" ", @missing)));
|
&install_error(&text('download_ecpan',
|
||||||
|
&html_escape(join(" ", @missing))));
|
||||||
}
|
}
|
||||||
$source = join("<br>", @source);
|
$source = join("<br>", @source);
|
||||||
|
|
||||||
@@ -192,14 +195,16 @@ elsif ($in{'source'} == 3) {
|
|||||||
&ftp_download($host, $file, $pfile, \$error,
|
&ftp_download($host, $file, $pfile, \$error,
|
||||||
\&progress_callback);
|
\&progress_callback);
|
||||||
}
|
}
|
||||||
else { &install_error(&text('download_eurl', $m)); }
|
else {
|
||||||
|
&install_error(&text('download_eurl',&html_escape($m)));
|
||||||
|
}
|
||||||
&install_error($error) if ($error);
|
&install_error($error) if ($error);
|
||||||
push(@pfile, $pfile);
|
push(@pfile, $pfile);
|
||||||
}
|
}
|
||||||
$need_unlink = 1;
|
$need_unlink = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
&error("Unknown source mode $in{'source'}");
|
&error("Unknown source mode ".&html_escape($in{'source'}));
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if the file looks like a perl module
|
# Check if the file looks like a perl module
|
||||||
@@ -287,7 +292,7 @@ foreach $d (@dirs) {
|
|||||||
close(MAKEFILE);
|
close(MAKEFILE);
|
||||||
push(@allreqs, @prereqs);
|
push(@allreqs, @prereqs);
|
||||||
}
|
}
|
||||||
system("rm -rf $mtemp");
|
&unlink_file($mtemp);
|
||||||
|
|
||||||
# Work out which pre-requesites are missing
|
# Work out which pre-requesites are missing
|
||||||
@allreqs = &unique(@allreqs);
|
@allreqs = &unique(@allreqs);
|
||||||
|
|||||||
@@ -1567,28 +1567,31 @@ if (!$gconfig{'tempdelete_days'}) {
|
|||||||
print STDERR "Temp file clearing is disabled\n";
|
print STDERR "Temp file clearing is disabled\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Cleanup files in /tmp/.webmin
|
||||||
if ($gconfig{'tempdir'} && !$gconfig{'tempdirdelete'}) {
|
if ($gconfig{'tempdir'} && !$gconfig{'tempdirdelete'}) {
|
||||||
print STDERR "Temp file clearing is not done for the custom directory $gconfig{'tempdir'}\n";
|
print STDERR "Temp file clearing is not done for the custom directory $gconfig{'tempdir'}\n";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
local $tempdir = &transname();
|
local $tempdir = &transname();
|
||||||
$tempdir =~ s/\/([^\/]+)$//;
|
$tempdir =~ s/\/([^\/]+)$//;
|
||||||
if (!$tempdir || $tempdir eq "/") {
|
if (!$tempdir || $tempdir eq "/") {
|
||||||
$tempdir = "/tmp/.webmin";
|
$tempdir = "/tmp/.webmin";
|
||||||
}
|
|
||||||
|
|
||||||
local $cutoff = time() - $gconfig{'tempdelete_days'}*24*60*60;
|
|
||||||
opendir(DIR, $tempdir);
|
|
||||||
foreach my $f (readdir(DIR)) {
|
|
||||||
next if ($f eq "." || $f eq "..");
|
|
||||||
local @st = lstat("$tempdir/$f");
|
|
||||||
if ($st[9] < $cutoff) {
|
|
||||||
&unlink_file("$tempdir/$f");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
closedir(DIR);
|
|
||||||
|
|
||||||
|
local $cutoff = time() - $gconfig{'tempdelete_days'}*24*60*60;
|
||||||
|
opendir(DIR, $tempdir);
|
||||||
|
foreach my $f (readdir(DIR)) {
|
||||||
|
next if ($f eq "." || $f eq "..");
|
||||||
|
local @st = lstat("$tempdir/$f");
|
||||||
|
if ($st[9] < $cutoff) {
|
||||||
|
&unlink_file("$tempdir/$f");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete stale lock files
|
||||||
my $lockdir = $var_directory."/locks";
|
my $lockdir = $var_directory."/locks";
|
||||||
opendir(DIR, $lockdir);
|
opendir(DIR, $lockdir);
|
||||||
foreach my $f (readdir(DIR)) {
|
foreach my $f (readdir(DIR)) {
|
||||||
@@ -1600,6 +1603,11 @@ foreach my $f (readdir(DIR)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(DIR);
|
closedir(DIR);
|
||||||
|
|
||||||
|
# Cleanup old websockets
|
||||||
|
foreach (&get_miniserv_websockets_modules()) {
|
||||||
|
&cleanup_miniserv_websockets(undef, $_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=head2 list_cron_files()
|
=head2 list_cron_files()
|
||||||
|
|||||||
@@ -349,8 +349,8 @@ if ($fh6) {
|
|||||||
}
|
}
|
||||||
while(1) {
|
while(1) {
|
||||||
$$port++;
|
$$port++;
|
||||||
if ($$port >= 65536) {
|
if ($$port < 0 || $$port > 65535) {
|
||||||
return "Failed to allocate a free port!";
|
return "Failed to allocate a free port number: $$port";
|
||||||
}
|
}
|
||||||
$pack = pack_sockaddr_in($$port, INADDR_ANY);
|
$pack = pack_sockaddr_in($$port, INADDR_ANY);
|
||||||
next if (!bind($fh, $pack));
|
next if (!bind($fh, $pack));
|
||||||
@@ -366,4 +366,3 @@ if ($fh6) {
|
|||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1 @@
|
|||||||
body blockquote:not([style*="border-left"]) {
|
body blockquote:not([style*="border-left"]){border-left:1px solid #ccc;margin-left:6px;margin-top:0;margin-bottom:0;padding-left:12px;}pre{white-space:break-spaces;margin:0 0 4px 0}
|
||||||
border-left: 1px solid #ccc;
|
|
||||||
margin-left: 6px;
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-left: 12px;
|
|
||||||
}
|
|
||||||
@@ -29,9 +29,6 @@ table.ui_table thead td {
|
|||||||
table.sortable tbody td {
|
table.sortable tbody td {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
table.ui_table td * {
|
|
||||||
line-height:1.5em;
|
|
||||||
}
|
|
||||||
table.ui_table td textarea {line-height:normal; font-family:monospace;}
|
table.ui_table td textarea {line-height:normal; font-family:monospace;}
|
||||||
table.ui_table td div.barchart * {
|
table.ui_table td div.barchart * {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -55,9 +52,16 @@ div.ui_form_end_buttons {
|
|||||||
padding: 6px 3px;
|
padding: 6px 3px;
|
||||||
}
|
}
|
||||||
.ui_form_end_buttons input {
|
.ui_form_end_buttons input {
|
||||||
padding: 2px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
table.ui_grid_table td { padding: 2px 4px; }
|
.ui_form_value td > b > input+label+tt,
|
||||||
|
.ui_form_value td > b > input+label {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
|
table.ui_grid_table td {
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
select,
|
||||||
input {
|
input {
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2371,6 +2371,8 @@ if (ref($opts)) {
|
|||||||
&print_tempfile(CFILE, "ExecReload=$kill -HUP \$MAINPID\n") if ($opts->{'reload'} eq '0');
|
&print_tempfile(CFILE, "ExecReload=$kill -HUP \$MAINPID\n") if ($opts->{'reload'} eq '0');
|
||||||
&print_tempfile(CFILE, "ExecStop=$opts->{'stop'}\n") if ($opts->{'stop'});
|
&print_tempfile(CFILE, "ExecStop=$opts->{'stop'}\n") if ($opts->{'stop'});
|
||||||
&print_tempfile(CFILE, "ExecReload=$opts->{'reload'}\n") if ($opts->{'reload'});
|
&print_tempfile(CFILE, "ExecReload=$opts->{'reload'}\n") if ($opts->{'reload'});
|
||||||
|
&print_tempfile(CFILE, "ExecStartPre=$opts->{'startpre'}\n") if ($opts->{'startpre'});
|
||||||
|
&print_tempfile(CFILE, "ExecStartPost=$opts->{'startpost'}\n") if ($opts->{'startpost'});
|
||||||
&print_tempfile(CFILE, "Type=$opts->{'type'}\n") if ($opts->{'type'});
|
&print_tempfile(CFILE, "Type=$opts->{'type'}\n") if ($opts->{'type'});
|
||||||
&print_tempfile(CFILE, "Environment=\"$opts->{'env'}\"\n") if ($opts->{'env'});
|
&print_tempfile(CFILE, "Environment=\"$opts->{'env'}\"\n") if ($opts->{'env'});
|
||||||
&print_tempfile(CFILE, "User=$opts->{'user'}\n") if ($opts->{'user'});
|
&print_tempfile(CFILE, "User=$opts->{'user'}\n") if ($opts->{'user'});
|
||||||
@@ -2381,8 +2383,8 @@ if (ref($opts)) {
|
|||||||
&print_tempfile(CFILE, "RestartSec=$opts->{'restartsec'}\n") if ($opts->{'restartsec'});
|
&print_tempfile(CFILE, "RestartSec=$opts->{'restartsec'}\n") if ($opts->{'restartsec'});
|
||||||
&print_tempfile(CFILE, "TimeoutSec=$opts->{'timeout'}\n") if ($opts->{'timeout'});
|
&print_tempfile(CFILE, "TimeoutSec=$opts->{'timeout'}\n") if ($opts->{'timeout'});
|
||||||
&print_tempfile(CFILE, "TimeoutStopSec=$opts->{'timeoutstopsec'}\n") if ($opts->{'timeoutstopsec'});
|
&print_tempfile(CFILE, "TimeoutStopSec=$opts->{'timeoutstopsec'}\n") if ($opts->{'timeoutstopsec'});
|
||||||
&print_tempfile(CFILE, "StandardOutput=file:$opts->{'logstd'}\n") if ($opts->{'logstd'});
|
&print_tempfile(CFILE, "StandardOutput=".($opts->{'logstd'} =~ /^\// ? 'file:' : '')."$opts->{'logstd'}\n") if ($opts->{'logstd'});
|
||||||
&print_tempfile(CFILE, "StandardError=file:$opts->{'logerr'}\n") if ($opts->{'logerr'});
|
&print_tempfile(CFILE, "StandardError=".($opts->{'logerr'} =~ /^\// ? 'file:' : '')."$opts->{'logerr'}\n") if ($opts->{'logerr'});
|
||||||
}
|
}
|
||||||
|
|
||||||
&print_tempfile(CFILE, "\n");
|
&print_tempfile(CFILE, "\n");
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
skip_index=1
|
||||||
lines=100
|
lines=100
|
||||||
others=1
|
others=1
|
||||||
reverse=1
|
reverse=1
|
||||||
|
log_any=0
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
skip_index=Open log view on module load,1,1-Yes,0-No
|
||||||
lines=Default number of lines to display,0,6
|
lines=Default number of lines to display,0,6
|
||||||
refresh=Seconds between log view refreshes,3,Never
|
refresh=Seconds between log view refreshes,3,Never
|
||||||
others=Show logs from other modules?,1,1-Yes,0-No
|
others=Show logs from other modules,1,1-Yes,0-No
|
||||||
extras=Extra log files to show,9,50,4,\t
|
extras=Extra log files to show,9,50,4,\t
|
||||||
reverse=Log display order,1,1-Newest lines at top,0-Newest lines at bottom
|
reverse=Log display order,1,1-Newest lines at top,0-Newest lines at bottom
|
||||||
|
log_any=Can view any file as a log,1,1-Yes,0-No
|
||||||
|
|||||||
@@ -4,26 +4,24 @@
|
|||||||
|
|
||||||
require './logviewer-lib.pl';
|
require './logviewer-lib.pl';
|
||||||
|
|
||||||
&ui_print_header($text{'index_subtitle'}, $text{'index_title'}, "", undef, 1, 1, 0,
|
|
||||||
&help_search_link("systemd-journal journalctl", "man", "doc"));
|
|
||||||
|
|
||||||
if (!&has_command('journalctl')) {
|
|
||||||
# Not installed
|
|
||||||
&ui_print_endpage(&text('index_econf', "<tt>$config{'syslog_conf'}</tt>", "../config.cgi?$module_name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
# Display syslog rules
|
# Display syslog rules
|
||||||
|
my @col0;
|
||||||
my @col1;
|
my @col1;
|
||||||
my @col2;
|
my @col2;
|
||||||
my @col3;
|
my @col3;
|
||||||
|
my @lnks;
|
||||||
if ($access{'syslog'}) {
|
if ($access{'syslog'}) {
|
||||||
my @systemctl_cmds = &get_systemctl_cmds();
|
my @systemctl_cmds = &get_systemctl_cmds();
|
||||||
foreach $o (@systemctl_cmds) {
|
foreach $o (@systemctl_cmds) {
|
||||||
local @cols;
|
local @cols;
|
||||||
push(@cols, &text('index_cmd', "<tt>".$o->{'cmd'}."</tt>"));
|
push(@cols, &text('index_cmd', "<tt>".
|
||||||
push(@cols, $o->{'desc'});
|
&cleanup_destination($o->{'cmd'})."</tt>"));
|
||||||
push(@cols, &ui_link("view_log.cgi?idx=$o->{'id'}&view=1", $text{'index_view'}) );
|
my $icon = $o->{'id'} =~ /journal-(a|x)/ ? "◦ " : "";
|
||||||
push(@col1, \@cols);
|
push(@cols, $icon.&cleanup_description($o->{'desc'}));
|
||||||
|
push(@cols, &ui_link("view_log.cgi?idx=$o->{'id'}&view=1",
|
||||||
|
$text{'index_view'}) );
|
||||||
|
push(@lnks, "view_log.cgi?idx=$o->{'id'}&view=1");
|
||||||
|
push(@col0, \@cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
# System logs from other modules
|
# System logs from other modules
|
||||||
@@ -47,6 +45,8 @@ if ($access{'syslog'}) {
|
|||||||
map { &html_escape($_) } @{$c->{'sel'}}));
|
map { &html_escape($_) } @{$c->{'sel'}}));
|
||||||
push(@cols, &ui_link("view_log.cgi?idx=syslog-".
|
push(@cols, &ui_link("view_log.cgi?idx=syslog-".
|
||||||
$c->{'index'}."&"."view=1", $text{'index_view'}) );
|
$c->{'index'}."&"."view=1", $text{'index_view'}) );
|
||||||
|
push(@lnks, "view_log.cgi?idx=syslog-".
|
||||||
|
$c->{'index'}."&"."view=1");
|
||||||
push(@col1, \@cols);
|
push(@col1, \@cols);
|
||||||
push(@foreign_syslogs, $c->{'file'});
|
push(@foreign_syslogs, $c->{'file'});
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,9 @@ if ($access{'syslog'}) {
|
|||||||
"view_log.cgi?idx=syslog-ng-".
|
"view_log.cgi?idx=syslog-ng-".
|
||||||
$dest->{'index'}."&"."view=1",
|
$dest->{'index'}."&"."view=1",
|
||||||
$text{'index_view'}) );
|
$text{'index_view'}) );
|
||||||
|
push(@lnks, "view_log.cgi?idx=syslog-ng-".
|
||||||
|
$dest->{'index'}."&"."view=1");
|
||||||
|
@cols = sort { $a->[2] cmp $b->[2] } @cols;
|
||||||
push(@col1, \@cols);
|
push(@col1, \@cols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,9 +98,12 @@ if ($config{'others'} && $access{'others'}) {
|
|||||||
push(@cols, &text('index_cmd',
|
push(@cols, &text('index_cmd',
|
||||||
"<tt>".&html_escape($o->{'cmd'})."</tt>"));
|
"<tt>".&html_escape($o->{'cmd'})."</tt>"));
|
||||||
}
|
}
|
||||||
push(@cols, &html_escape($o->{'desc'}));
|
push(@cols, $o->{'desc'} ? &html_escape($o->{'desc'}) : "");
|
||||||
push(@cols, &ui_link("view_log.cgi?oidx=$o->{'mindex'}".
|
push(@cols, &ui_link("view_log.cgi?oidx=$o->{'mindex'}".
|
||||||
"&omod=$o->{'mod'}&view=1", $text{'index_view'}) );
|
"&omod=$o->{'mod'}&view=1", $text{'index_view'}) );
|
||||||
|
push(@lnks, "view_log.cgi?oidx=$o->{'mindex'}".
|
||||||
|
"&omod=$o->{'mod'}&view=1");
|
||||||
|
@cols = sort { $a->[2] cmp $b->[2] } @cols;
|
||||||
push(@col2, \@cols);
|
push(@col2, \@cols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,29 +120,49 @@ foreach $e (&extra_log_files()) {
|
|||||||
push(@cols, &text('index_cmd',
|
push(@cols, &text('index_cmd',
|
||||||
"<tt>".&html_escape($e->{'cmd'})."</tt>"));
|
"<tt>".&html_escape($e->{'cmd'})."</tt>"));
|
||||||
}
|
}
|
||||||
push(@cols, &html_escape($e->{'desc'}));
|
push(@cols, $e->{'desc'} ? &html_escape($e->{'desc'}) : "");
|
||||||
push(@cols, &ui_link("view_log.cgi?extra=".&urlize($e->{'file'} || $e->{'cmd'})."&view=1", $text{'index_view'}) );
|
push(@cols, &ui_link("view_log.cgi?extra=".&urlize($e->{'file'} || $e->{'cmd'})."&view=1", $text{'index_view'}) );
|
||||||
|
push(@lnks, "view_log.cgi?extra=".&urlize($e->{'file'} || $e->{'cmd'})."&view=1");
|
||||||
|
@cols = sort { $a->[2] cmp $b->[2] } @cols;
|
||||||
push(@col3, \@cols);
|
push(@col3, \@cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Print sorted table with logs files and commands
|
# Print sorted table with logs files and commands
|
||||||
my @acols = (@col1, @col2, @col3);
|
my @acols = (@col0, @col1, @col2, @col3);
|
||||||
|
|
||||||
|
my $print_header = sub {
|
||||||
|
# Print the header
|
||||||
|
&ui_print_header($text{'index_subtitle'}, $text{'index_title'}, "", undef, 1, 1, 0,
|
||||||
|
&help_search_link("systemd-journal journalctl", "man", "doc"));
|
||||||
|
};
|
||||||
|
|
||||||
|
# If no logs are available just show the message
|
||||||
|
if (!@acols) {
|
||||||
|
$print_header->();
|
||||||
|
&ui_print_endpage($text{'index_elogs'});
|
||||||
|
}
|
||||||
|
|
||||||
|
# If we jump directly to logs just redirect
|
||||||
|
if ($config{'skip_index'} == 1) {
|
||||||
|
if ($lnks[0]) {
|
||||||
|
&redirect($lnks[0]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print the header
|
||||||
|
$print_header->();
|
||||||
|
|
||||||
print &ui_columns_start( @acols ? [
|
print &ui_columns_start( @acols ? [
|
||||||
$text{'index_to'},
|
$text{'index_to'},
|
||||||
$text{'index_rule'}, "" ] : [ ], 100);
|
$text{'index_rule'}, "" ] : [ ], 100);
|
||||||
if (@acols) {
|
foreach my $col (@acols) {
|
||||||
@acols = sort { $a->[2] cmp $b->[2] } @acols;
|
print &ui_columns_row($col);
|
||||||
foreach my $col (@acols) {
|
|
||||||
print &ui_columns_row($col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
print &ui_columns_row([$text{'index_elogs'}], [" colspan='3' style='text-align: center'"], 3);
|
|
||||||
}
|
}
|
||||||
print &ui_columns_end();
|
print &ui_columns_end();
|
||||||
print "<p>\n";
|
print "<p>\n";
|
||||||
|
|
||||||
if ($access{'any'}) {
|
if ($access{'any'} && $config{'log_any'} == 1) {
|
||||||
# Can view any log (under allowed dirs)
|
# Can view any log (under allowed dirs)
|
||||||
print &ui_form_start("view_log.cgi");
|
print &ui_form_start("view_log.cgi");
|
||||||
print &ui_hidden("view", 1),"\n";
|
print &ui_hidden("view", 1),"\n";
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
index_title=System Logs Viewer
|
index_title=System Logs
|
||||||
index_elogs=No logs were found to display
|
index_elogs=The <tt>journalctl</tt> command is not available on your system, and other logs are configured not to be displayed in the module configuration.
|
||||||
index_to=Log destination
|
index_to=Log
|
||||||
index_rule=Messages selected
|
index_rule=Description
|
||||||
index_file=File $1
|
index_file=File $1
|
||||||
index_cmd=Output from $1
|
index_cmd=Output from $1
|
||||||
index_return=system logs viewer
|
index_return=system logs
|
||||||
index_view=View..
|
index_view=View..
|
||||||
index_viewfile=View log file:
|
index_viewfile=View log file:
|
||||||
index_viewok=View
|
index_viewok=View
|
||||||
@@ -16,16 +16,35 @@ journal_journalctl_debug_info=Debug and info messages
|
|||||||
journal_journalctl_notice_warning=Notice and warning messages
|
journal_journalctl_notice_warning=Notice and warning messages
|
||||||
journal_journalctl_err_crit=Error and critical messages
|
journal_journalctl_err_crit=Error and critical messages
|
||||||
journal_journalctl_alert_emerg=Alert and emergency messages
|
journal_journalctl_alert_emerg=Alert and emergency messages
|
||||||
|
journal_journalctl_unit=Messages for specific unit
|
||||||
|
journal_since0=Latest available
|
||||||
|
journal_since1=Real-time follow
|
||||||
|
journal_since2=Current boot
|
||||||
|
journal_since3=7 days ago
|
||||||
|
journal_since4=24 hours ago
|
||||||
|
journal_since5=8 hours ago
|
||||||
|
journal_since6=1 hour ago
|
||||||
|
journal_since7=30 minutes ago
|
||||||
|
journal_since8=10 minutes ago
|
||||||
|
journal_since9=3 minutes ago
|
||||||
|
journal_since10=1 minute ago
|
||||||
|
journal_sincefollow=in
|
||||||
|
journal_since=since
|
||||||
|
|
||||||
view_title=View Logfile
|
view_title=View Logfile
|
||||||
|
view_titlejournal=View Journal
|
||||||
view_header=Last $1 lines of $2
|
view_header=Last $1 lines of $2
|
||||||
view_header2=Last $1 lines
|
view_header2=Last $1 lines
|
||||||
|
view_header3=Lines of $1
|
||||||
view_empty=Log file is empty
|
view_empty=Log file is empty
|
||||||
view_refresh=Refresh
|
view_loading=Log file is being watched .. No new lines yet.
|
||||||
view_filter=Filter lines with text $1
|
view_filter=Filter lines with text $1
|
||||||
|
view_filter_btn=Filter
|
||||||
|
|
||||||
save_efile='$1' is not a valid filename : $2
|
save_efile='$1' is not a valid filename : $2
|
||||||
save_ecannot2=You are not allowed to view this log
|
save_ecannot2=You are not allowed to view this log
|
||||||
|
save_ecannot3=Error: You are not allowed to view this log
|
||||||
|
save_ecannot4=Error: Could not open '$1'
|
||||||
save_ecannot6=You are not allowed to view arbitrary logs
|
save_ecannot6=You are not allowed to view arbitrary logs
|
||||||
save_ecannot7=You are not allowed to view this extra log
|
save_ecannot7=You are not allowed to view this extra log
|
||||||
save_emissing=Missing log file to view
|
save_emissing=Missing log file to view
|
||||||
|
|||||||
@@ -27,33 +27,139 @@ foreach $f (@files) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
# get_journal_since
|
||||||
# Returns standard
|
# Returns a list of journalctl since commands
|
||||||
|
sub get_journal_since
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
{ "" => $text{'journal_since0'} },
|
||||||
|
{ "-f" => $text{'journal_since1'} },
|
||||||
|
{ "-b" => $text{'journal_since2'} },
|
||||||
|
{ "-S '7 days ago'" => $text{'journal_since3'} },
|
||||||
|
{ "-S '24 hours ago'" => $text{'journal_since4'} },
|
||||||
|
{ "-S '8 hours ago'" => $text{'journal_since5'} },
|
||||||
|
{ "-S '1 hour ago'" => $text{'journal_since6'} },
|
||||||
|
{ "-S '30 minutes ago'" => $text{'journal_since7'} },
|
||||||
|
{ "-S '10 minutes ago'" => $text{'journal_since8'} },
|
||||||
|
{ "-S '3 minutes ago'" => $text{'journal_since9'} },
|
||||||
|
{ "-S '1 minute ago'" => $text{'journal_since10'} },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
# get_systemctl_cmds([force-select])
|
||||||
|
# Returns logs for journalctl
|
||||||
sub get_systemctl_cmds
|
sub get_systemctl_cmds
|
||||||
{
|
{
|
||||||
my $lines = $config{'lines'} || 1000;
|
my $fselect = shift;
|
||||||
return !&has_command('journalctl') ? () : (
|
my $lines = $in{'lines'} ? int($in{'lines'}) : int($config{'lines'}) || 1000;
|
||||||
{ 'cmd' => "journalctl --lines $lines -p alert..emerg",
|
my $journalctl_cmd = &has_command('journalctl');
|
||||||
'desc' => $text{'journal_journalctl_alert_emerg'},
|
return () if (!$journalctl_cmd);
|
||||||
'id' => "journal-1", },
|
my @rs = (
|
||||||
{ 'cmd' => "journalctl --lines $lines -p err..crit",
|
{ 'cmd' => "journalctl -n $lines",
|
||||||
'desc' => $text{'journal_journalctl_err_crit'},
|
|
||||||
'id' => "journal-2", },
|
|
||||||
{ 'cmd' => "journalctl --lines $lines -p notice..warning",
|
|
||||||
'desc' => $text{'journal_journalctl_notice_warning'},
|
|
||||||
'id' => "journal-3", },
|
|
||||||
{ 'cmd' => "journalctl --lines $lines -p debug..info",
|
|
||||||
'desc' => $text{'journal_journalctl_debug_info'},
|
|
||||||
'id' => "journal-4", },
|
|
||||||
{ 'cmd' => "journalctl --lines $lines -k ",
|
|
||||||
'desc' => $text{'journal_journalctl_dmesg'},
|
|
||||||
'id' => "journal-5", },
|
|
||||||
{ 'cmd' => "journalctl --lines $lines -x ",
|
|
||||||
'desc' => $text{'journal_expla_journalctl'},
|
|
||||||
'id' => "journal-6", },
|
|
||||||
{ 'cmd' => "journalctl --lines $lines",
|
|
||||||
'desc' => $text{'journal_journalctl'},
|
'desc' => $text{'journal_journalctl'},
|
||||||
|
'id' => "journal-1", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -x ",
|
||||||
|
'desc' => $text{'journal_expla_journalctl'},
|
||||||
|
'id' => "journal-2", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -p alert..emerg",
|
||||||
|
'desc' => $text{'journal_journalctl_alert_emerg'},
|
||||||
|
'id' => "journal-3", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -p err..crit",
|
||||||
|
'desc' => $text{'journal_journalctl_err_crit'},
|
||||||
|
'id' => "journal-4", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -p notice..warning",
|
||||||
|
'desc' => $text{'journal_journalctl_notice_warning'},
|
||||||
|
'id' => "journal-5", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -p debug..info",
|
||||||
|
'desc' => $text{'journal_journalctl_debug_info'},
|
||||||
|
'id' => "journal-6", },
|
||||||
|
{ 'cmd' => "journalctl -n $lines -k ",
|
||||||
|
'desc' => $text{'journal_journalctl_dmesg'},
|
||||||
'id' => "journal-7", } );
|
'id' => "journal-7", } );
|
||||||
|
|
||||||
|
# Add more units from config if exists on the system
|
||||||
|
my (%ucache, %uread);
|
||||||
|
my $units_cache = "$module_config_directory/units.cache";
|
||||||
|
&read_file($units_cache, \%ucache);
|
||||||
|
if (!%ucache) {
|
||||||
|
my $out = &backquote_command("systemctl list-units --all --no-legend ".
|
||||||
|
"--no-pager");
|
||||||
|
foreach my $line (split(/\r?\n/, $out)) {
|
||||||
|
$line =~ s/^[^a-z0-9\-\_\.]+//i;
|
||||||
|
my ($unit, $desc) = (split(/\s+/, $line, 5))[0, 4];
|
||||||
|
$uread{$unit} = $desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# All units
|
||||||
|
%ucache = %uread if (%uread);
|
||||||
|
# If forced to select, return full list
|
||||||
|
if ($fselect) {
|
||||||
|
my %units = %uread ? %uread : %ucache;
|
||||||
|
foreach my $u (sort keys %units) {
|
||||||
|
my $uname = $u;
|
||||||
|
$uname =~ s/\\x([0-9A-Fa-f]{2})/pack('H2', $1)/eg;
|
||||||
|
push(@rs, { 'cmd' => "journalctl -n ".
|
||||||
|
"$lines -u $u",
|
||||||
|
'desc' => $uname,
|
||||||
|
'id' => "journal-a-$u", });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Otherwise, return only the pointer
|
||||||
|
# element for the index page
|
||||||
|
else {
|
||||||
|
push(@rs,
|
||||||
|
{ 'cmd' => "journalctl -n $lines -u",
|
||||||
|
'desc' => $text{'journal_journalctl_unit'},
|
||||||
|
'id' => "journal-u" });
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save cache
|
||||||
|
if (%uread) {
|
||||||
|
&lock_file($units_cache);
|
||||||
|
&write_file($units_cache, \%ucache);
|
||||||
|
&unlock_file($units_cache);
|
||||||
|
}
|
||||||
|
return @rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
# clear_systemctl_cache()
|
||||||
|
# Clear the cache of systemctl units
|
||||||
|
sub clear_systemctl_cache
|
||||||
|
{
|
||||||
|
unlink("$module_config_directory/units.cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
# cleanup_destination(cmd)
|
||||||
|
# Returns a destination of some command cleaned up for display
|
||||||
|
sub cleanup_destination
|
||||||
|
{
|
||||||
|
my $cmd = shift;
|
||||||
|
$cmd =~ s/-n\s+\d+\s*//;
|
||||||
|
$cmd =~ s/\.service$//;
|
||||||
|
return $cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
# cleanup_description(desc)
|
||||||
|
# Returns a description cleaned up for display
|
||||||
|
sub cleanup_description
|
||||||
|
{
|
||||||
|
my $desc = shift;
|
||||||
|
$desc =~ s/\s+\(Virtualmin\)//;
|
||||||
|
return $desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
# fix_clashing_description(description, service)
|
||||||
|
# Returns known clashing descriptions fixed
|
||||||
|
sub fix_clashing_description
|
||||||
|
{
|
||||||
|
my ($desc, $serv) = @_;
|
||||||
|
# EL systems name for PHP FastCGI Process Manager is repeated
|
||||||
|
if ($serv =~ /php(\d+)-php-fpm/) {
|
||||||
|
my $php_version = $1;
|
||||||
|
$php_version = join(".", split(//, $php_version));
|
||||||
|
$desc =~ s/PHP/PHP $php_version/;
|
||||||
|
}
|
||||||
|
return $desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
# all_log_files(file)
|
# all_log_files(file)
|
||||||
@@ -138,5 +244,12 @@ foreach my $f (@rv) {
|
|||||||
return @rv;
|
return @rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# config_post_save
|
||||||
|
# Called after the module's configuration has been saved
|
||||||
|
sub config_post_save
|
||||||
|
{
|
||||||
|
&clear_systemctl_cache();
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name=logviewer
|
name=logviewer
|
||||||
category=system
|
category=system
|
||||||
os_support=*-linux
|
os_support=*-linux
|
||||||
desc=System Logs Viewer
|
desc=System Logs
|
||||||
depends=proc
|
depends=proc
|
||||||
longdesc=View and search all logs available on system
|
longdesc=View and search all logs available on system
|
||||||
readonly=1
|
readonly=1
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ require './logviewer-lib.pl';
|
|||||||
&foreign_require("proc", "proc-lib.pl");
|
&foreign_require("proc", "proc-lib.pl");
|
||||||
|
|
||||||
# Viewing a log file
|
# Viewing a log file
|
||||||
@extras = &extra_log_files();
|
my @extras = &extra_log_files();
|
||||||
if ($in{'idx'} =~ /^\//) {
|
if ($in{'idx'} =~ /^\//) {
|
||||||
# The drop-down selector on this page has chosen a file
|
# The drop-down selector on this page has chosen a file
|
||||||
if (&indexof($in{'idx'}, (map { $_->{'file'} } @extras)) >= 0) {
|
if (&indexof($in{'idx'}, (map { $_->{'file'} } @extras)) >= 0) {
|
||||||
@@ -21,11 +21,31 @@ if ($in{'idx'} =~ /^\//) {
|
|||||||
delete($in{'idx'});
|
delete($in{'idx'});
|
||||||
delete($in{'oidx'});
|
delete($in{'oidx'});
|
||||||
}
|
}
|
||||||
|
my $journal_since = &get_journal_since();
|
||||||
if ($in{'idx'} ne '') {
|
if ($in{'idx'} ne '') {
|
||||||
# From systemctl commands
|
# From systemctl commands
|
||||||
if ($in{'idx'} =~ /^journal-/) {
|
if ($in{'idx'} =~ /^journal-/) {
|
||||||
my @systemctl_cmds = &get_systemctl_cmds();
|
my @systemctl_cmds = &get_systemctl_cmds(1);
|
||||||
my ($log) = grep { $_->{'id'} eq $in{'idx'} } @systemctl_cmds;
|
my ($log);
|
||||||
|
if ($in{'idx'} eq 'journal-u') {
|
||||||
|
($log) = grep { $_->{'cmd'} =~ /-u\s+\w+/ }
|
||||||
|
@systemctl_cmds;
|
||||||
|
$in{'idx'} = $log->{'id'};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
($log) = grep { $_->{'id'} eq $in{'idx'} }
|
||||||
|
@systemctl_cmds;
|
||||||
|
}
|
||||||
|
# If reverse is set, add it to the command
|
||||||
|
if ($reverse) {
|
||||||
|
$log->{'cmd'} .= " -r";
|
||||||
|
}
|
||||||
|
# If since is set and allowed, add it to the command
|
||||||
|
if ($in{'since'} &&
|
||||||
|
grep { $_ eq $in{'since'} }
|
||||||
|
map { keys %$_ } @$journal_since) {
|
||||||
|
$log->{'cmd'} .= " $in{'since'}";
|
||||||
|
}
|
||||||
&can_edit_log($log) && $access{'syslog'} ||
|
&can_edit_log($log) && $access{'syslog'} ||
|
||||||
&error($text{'save_ecannot2'});
|
&error($text{'save_ecannot2'});
|
||||||
$cmd = $log->{'cmd'};
|
$cmd = $log->{'cmd'};
|
||||||
@@ -94,99 +114,204 @@ else {
|
|||||||
}
|
}
|
||||||
print "Refresh: $config{'refresh'}\r\n"
|
print "Refresh: $config{'refresh'}\r\n"
|
||||||
if ($config{'refresh'});
|
if ($config{'refresh'});
|
||||||
&ui_print_header("<tt>".&html_escape($file || $cmd)."</tt>",
|
my $lines = $in{'lines'} ? int($in{'lines'}) : int($config{'lines'});
|
||||||
$in{'linktitle'} || $text{'view_title'}, "", undef, undef, $in{'nonavlinks'});
|
my $jfilter = $in{'filter'} ? $in{'filter'} : "";
|
||||||
|
my $filter = $jfilter ? quotemeta($jfilter) : "";
|
||||||
$lines = $in{'lines'} ? int($in{'lines'}) : int($config{'lines'});
|
my $reverse = $config{'reverse'} ? 1 : 0;
|
||||||
$filter = $in{'filter'} ? quotemeta($in{'filter'}) : "";
|
my $follow = $in{'since'} eq '-f' ? 1 : 0;
|
||||||
|
my $no_navlinks = $in{'nonavlinks'} == 1 ? 1 : undef;
|
||||||
|
my $skip_index = $config{'skip_index'} == 1 ? 1 : undef;
|
||||||
|
my $help_link = (!$no_navlinks && $skip_index) ?
|
||||||
|
&help_search_link("systemd-journal journalctl", "man", "doc") : undef;
|
||||||
|
my $no_links = $no_navlinks || $skip_index;
|
||||||
|
my $cmd_unpacked = $cmd;
|
||||||
|
$cmd_unpacked =~ s/\\x([0-9A-Fa-f]{2})/pack('H2', $1)/eg;
|
||||||
|
$cmd_unpacked =~ s/\s+\-r// if ($follow);
|
||||||
|
$cmd_unpacked =~ s/\s+\-n\s+\d+// if ($follow);
|
||||||
|
$cmd_unpacked .= " -g \"@{[&html_escape($jfilter)]}\"" if ($filter);
|
||||||
|
my $view_title = $in{'idx'} =~ /^journal/ ?
|
||||||
|
$text{'view_titlejournal'} : $text{'view_title'};
|
||||||
|
&ui_print_header("<tt>".&html_escape($file || $cmd_unpacked)."</tt>",
|
||||||
|
$in{'linktitle'} || $view_title, "", undef,
|
||||||
|
!$no_navlinks && $skip_index,
|
||||||
|
($no_navlinks || $skip_index) ? 1 : undef,
|
||||||
|
0, $help_link);
|
||||||
|
|
||||||
&filter_form();
|
&filter_form();
|
||||||
|
|
||||||
$| = 1;
|
# Standard output
|
||||||
print "<pre>";
|
if (!$follow) {
|
||||||
local $tailcmd = $config{'tail_cmd'} || "tail -n LINES";
|
$| = 1;
|
||||||
$tailcmd =~ s/LINES/$lines/g;
|
print "<pre>";
|
||||||
if ($filter ne "") {
|
local $tailcmd = $config{'tail_cmd'} || "tail -n LINES";
|
||||||
# Are we supposed to filter anything? Then use grep.
|
$tailcmd =~ s/LINES/$lines/g;
|
||||||
local @cats;
|
my ($safe_proc_out, $safe_proc_out_got);
|
||||||
if ($cmd) {
|
if ($filter ne "") {
|
||||||
# Getting output from a command
|
# Are we supposed to filter anything? Then use grep.
|
||||||
push(@cats, $cmd);
|
|
||||||
}
|
|
||||||
elsif ($config{'compressed'}) {
|
|
||||||
# All compressed versions
|
|
||||||
foreach $l (&all_log_files($file)) {
|
|
||||||
$c = &catter_command($l);
|
|
||||||
push(@cats, $c) if ($c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Just the one log
|
|
||||||
@cats = ( "cat ".quotemeta($file) );
|
|
||||||
}
|
|
||||||
$cat = "(".join(" ; ", @cats).")";
|
|
||||||
if ($config{'reverse'}) {
|
|
||||||
$tailcmd .= " | tac";
|
|
||||||
}
|
|
||||||
$eflag = $gconfig{'os_type'} =~ /-linux/ ? "-E" : "";
|
|
||||||
$dashflag = $gconfig{'os_type'} =~ /-linux/ ? "--" : "";
|
|
||||||
if (@cats) {
|
|
||||||
$got = &proc::safe_process_exec(
|
|
||||||
"$cat | grep -i -a $eflag $dashflag $filter ".
|
|
||||||
"| $tailcmd",
|
|
||||||
0, 0, STDOUT, undef, 1, 0, undef, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$got = undef;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
# Not filtering .. so cat the most recent non-empty file
|
|
||||||
if ($cmd) {
|
|
||||||
# Getting output from a command
|
|
||||||
$fullcmd = $cmd." | ".$tailcmd;
|
|
||||||
}
|
|
||||||
elsif ($config{'compressed'}) {
|
|
||||||
# Cat all compressed files
|
|
||||||
local @cats;
|
local @cats;
|
||||||
$total = 0;
|
if ($cmd) {
|
||||||
foreach $l (reverse(&all_log_files($file))) {
|
# Getting output from a command
|
||||||
next if (!-s $l);
|
push(@cats, $cmd);
|
||||||
$c = &catter_command($l);
|
}
|
||||||
if ($c) {
|
elsif ($config{'compressed'}) {
|
||||||
$len = int(&backquote_command(
|
# All compressed versions
|
||||||
"$c | wc -l"));
|
foreach $l (&all_log_files($file)) {
|
||||||
$total += $len;
|
$c = &catter_command($l);
|
||||||
push(@cats, $c);
|
push(@cats, $c) if ($c);
|
||||||
last if ($total > $in{'lines'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
# Just the one log
|
||||||
|
@cats = ( "cat ".quotemeta($file) );
|
||||||
|
}
|
||||||
|
$cat = "(".join(" ; ", @cats).")";
|
||||||
|
if ($reverse) {
|
||||||
|
$tailcmd .= " | tac" if ($cmd !~ /journalctl/);
|
||||||
|
}
|
||||||
|
$eflag = $gconfig{'os_type'} =~ /-linux/ ? "-E" : "";
|
||||||
|
$dashflag = $gconfig{'os_type'} =~ /-linux/ ? "--" : "";
|
||||||
if (@cats) {
|
if (@cats) {
|
||||||
$cat = "(".join(" ; ", reverse(@cats)).")";
|
my $fcmd;
|
||||||
$fullcmd = $cat." | ".$tailcmd;
|
if ($cmd =~ /journalctl/) {
|
||||||
|
$fcmd = "$cmd -g $filter";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$fcmd = "$cat | grep -i -a $eflag $dashflag $filter ".
|
||||||
|
"| $tailcmd";
|
||||||
|
}
|
||||||
|
open(my $output_fh, '>', \$safe_proc_out);
|
||||||
|
$safe_proc_out_got = &proc::safe_process_exec(
|
||||||
|
$fcmd, 0, 0, $output_fh, undef, 1, 0, undef, 1);
|
||||||
|
close($output_fh);
|
||||||
|
print $safe_proc_out if ($safe_proc_out !~ /-- No entries --/m);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$fullcmd = undef;
|
$safe_proc_out_got = undef;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# Not filtering .. so cat the most recent non-empty file
|
||||||
|
if ($cmd) {
|
||||||
|
# Getting output from a command
|
||||||
|
$fullcmd = $cmd.($cmd =~ /journalctl/ ? "" : (" | ".$tailcmd));
|
||||||
|
}
|
||||||
|
elsif ($config{'compressed'}) {
|
||||||
|
# Cat all compressed files
|
||||||
|
local @cats;
|
||||||
|
$total = 0;
|
||||||
|
foreach $l (reverse(&all_log_files($file))) {
|
||||||
|
next if (!-s $l);
|
||||||
|
$c = &catter_command($l);
|
||||||
|
if ($c) {
|
||||||
|
$len = int(&backquote_command(
|
||||||
|
"$c | wc -l"));
|
||||||
|
$total += $len;
|
||||||
|
push(@cats, $c);
|
||||||
|
last if ($total > $in{'lines'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (@cats) {
|
||||||
|
$cat = "(".join(" ; ", reverse(@cats)).")";
|
||||||
|
$fullcmd = $cat." | ".$tailcmd;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$fullcmd = undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Just run tail on the file
|
||||||
|
$fullcmd = $tailcmd." ".quotemeta($file);
|
||||||
|
}
|
||||||
|
if ($reverse && $fullcmd) {
|
||||||
|
$fullcmd .= " | tac" if ($fullcmd !~ /journalctl/);
|
||||||
|
}
|
||||||
|
if ($fullcmd) {
|
||||||
|
open(my $output_fh, '>', \$safe_proc_out);
|
||||||
|
$safe_proc_out_got = &proc::safe_process_exec(
|
||||||
|
$fullcmd, 0, 0, $output_fh, undef, 1, 0, undef, 1);
|
||||||
|
close($output_fh);
|
||||||
|
print $safe_proc_out if ($safe_proc_out !~ /-- No entries --/m);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$safe_proc_out_got = undef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
print "<i data-empty>$text{'view_empty'}</i>\n"
|
||||||
# Just run tail on the file
|
if (!$safe_proc_out_got || $safe_proc_out =~ /-- No entries --/m);
|
||||||
$fullcmd = $tailcmd." ".quotemeta($file);
|
print "</pre>\n";
|
||||||
}
|
}
|
||||||
if ($config{'reverse'} && $fullcmd) {
|
# Progressive output
|
||||||
$fullcmd .= " | tac";
|
else {
|
||||||
}
|
print "<pre id='logdata' data-reversed='$reverse'>";
|
||||||
if ($fullcmd) {
|
print "<i data-loading>$text{'view_loading'}</i>\n";
|
||||||
$got = &proc::safe_process_exec(
|
print "</pre>\n";
|
||||||
$fullcmd, 0, 0, STDOUT, undef, 1, 0, undef, 1);
|
my %tinfo = &get_theme_info($current_theme);
|
||||||
}
|
my $spa_theme = $tinfo{'spa'} ? 1 : 0;
|
||||||
else {
|
print <<EOF;
|
||||||
$got = undef;
|
<script>
|
||||||
}
|
// Abort previous log viewer progress fetch
|
||||||
|
if (typeof fn_logviewer_progress_abort === 'function') {
|
||||||
|
fn_logviewer_progress_abort();
|
||||||
|
}
|
||||||
|
// Update log viewer with new data from the server
|
||||||
|
(async function () {
|
||||||
|
const logviewer_progress_abort = new AbortController();
|
||||||
|
const logDataElement = document.getElementById("logdata"),
|
||||||
|
response = await fetch("view_log_progress.cgi?idx=$in{'idx'}&filter=$jfilter",
|
||||||
|
{ signal: logviewer_progress_abort.signal }),
|
||||||
|
reader = response.body.getReader(),
|
||||||
|
decoder = new TextDecoder("utf-8"),
|
||||||
|
processText = async function () {
|
||||||
|
let { done, value } = await reader.read();
|
||||||
|
while (!done) {
|
||||||
|
const chunk = decoder.decode(value, { stream: true }).trim(),
|
||||||
|
dataReversed = logDataElement.getAttribute("data-reversed");
|
||||||
|
if (!processText.started) {
|
||||||
|
processText.started = true;
|
||||||
|
const loadingElement = logDataElement.querySelector("i[data-loading]");
|
||||||
|
if (loadingElement) {
|
||||||
|
loadingElement.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let lines = chunk.split("\\n");
|
||||||
|
if (dataReversed === "1") {
|
||||||
|
lines = lines.reverse();
|
||||||
|
logDataElement.textContent =
|
||||||
|
lines.join("\\n") + "\\n" + logDataElement.textContent;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logDataElement.textContent += lines.join("\\n") + "\\n";
|
||||||
|
}
|
||||||
|
if (typeof fn_logviewer_progress_update === 'function') {
|
||||||
|
fn_logviewer_progress_update(chunk, dataReversed);
|
||||||
|
}
|
||||||
|
({ done, value } = await reader.read());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof fn_logviewer_progress_status === 'function') {
|
||||||
|
fn_logviewer_progress_status(response);
|
||||||
|
}
|
||||||
|
fn_logviewer_progress_abort = function () {
|
||||||
|
logviewer_progress_abort.abort();
|
||||||
|
fn_logviewer_progress_abort = null;
|
||||||
|
}
|
||||||
|
if ($spa_theme !== 1) {
|
||||||
|
window.onbeforeunload = function() {
|
||||||
|
if (typeof fn_logviewer_progress_abort === 'function') {
|
||||||
|
fn_logviewer_progress_abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
processText().catch((error) => {
|
||||||
|
if (typeof fn_logviewer_progress_ended === 'function') {
|
||||||
|
fn_logviewer_progress_ended(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
EOF
|
||||||
}
|
}
|
||||||
print "<i>$text{'view_empty'}</i>\n" if (!$got);
|
|
||||||
print "</pre>\n";
|
|
||||||
&filter_form();
|
&filter_form();
|
||||||
if ($in{'nonavlinks'}) {
|
if ($no_links) {
|
||||||
&ui_print_footer();
|
&ui_print_footer();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -196,7 +321,9 @@ else {
|
|||||||
sub filter_form
|
sub filter_form
|
||||||
{
|
{
|
||||||
print &ui_form_start("view_log.cgi");
|
print &ui_form_start("view_log.cgi");
|
||||||
print &ui_hidden("nonavlinks", $in{'nonavlinks'} ? 1 : 0),"\n";
|
if ($no_navlinks) {
|
||||||
|
print &ui_hidden("nonavlinks", $no_navlinks),"\n";
|
||||||
|
}
|
||||||
print &ui_hidden("linktitle", $in{'linktitle'}),"\n";
|
print &ui_hidden("linktitle", $in{'linktitle'}),"\n";
|
||||||
print &ui_hidden("oidx", $in{'oidx'}),"\n";
|
print &ui_hidden("oidx", $in{'oidx'}),"\n";
|
||||||
print &ui_hidden("omod", $in{'omod'}),"\n";
|
print &ui_hidden("omod", $in{'omod'}),"\n";
|
||||||
@@ -210,10 +337,11 @@ my $found = 0;
|
|||||||
my $text_view_header = 'view_header';
|
my $text_view_header = 'view_header';
|
||||||
if ($access{'syslog'}) {
|
if ($access{'syslog'}) {
|
||||||
# Logs from syslog
|
# Logs from syslog
|
||||||
my @systemctl_cmds = &get_systemctl_cmds();
|
my @systemctl_cmds = &get_systemctl_cmds(1);
|
||||||
foreach $c (@systemctl_cmds) {
|
foreach $c (@systemctl_cmds) {
|
||||||
next if (!&can_edit_log($c));
|
next if (!&can_edit_log($c));
|
||||||
push(@logfiles, [ $c->{'id'}, "$c->{'desc'}" ]);
|
my $icon = $c->{'id'} =~ /journal-(a|x)/ ? "◦ " : "";
|
||||||
|
push(@logfiles, [ $c->{'id'}, $icon.$c->{'desc'} ]);
|
||||||
$found++ if ($c->{'id'} eq $in{'idx'});
|
$found++ if ($c->{'id'} eq $in{'idx'});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,18 +395,35 @@ foreach $e (&extra_log_files()) {
|
|||||||
}
|
}
|
||||||
if (@logfiles && $found) {
|
if (@logfiles && $found) {
|
||||||
$sel = &ui_select("idx", $in{'idx'} eq '' ? $file : $in{'idx'},
|
$sel = &ui_select("idx", $in{'idx'} eq '' ? $file : $in{'idx'},
|
||||||
[ @logfiles ], undef, undef, undef, undef, "onChange='form.submit()'");
|
[ @logfiles ], undef, undef, undef, undef,
|
||||||
|
"onChange='form.submit()' style='max-width: 240px'");
|
||||||
|
if ($in{'idx'} =~ /^journal-/) {
|
||||||
|
my $since_label = $follow ? $text{'journal_sincefollow'} :
|
||||||
|
$text{'journal_since'};
|
||||||
|
$sel .= "$since_label " .
|
||||||
|
&ui_select("since", $in{'since'},
|
||||||
|
[ map { my ($key) = keys %$_;
|
||||||
|
[ $key, $_->{$key} ] }
|
||||||
|
@$journal_since ],
|
||||||
|
undef, undef, undef, undef,
|
||||||
|
"onChange='form.submit()'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$text_view_header = 'view_header2';
|
$text_view_header = 'view_header2';
|
||||||
print &ui_hidden("idx", $in{'idx'}),"\n";
|
print &ui_hidden("idx", $in{'idx'}),"\n";
|
||||||
}
|
}
|
||||||
|
if ($follow) {
|
||||||
print &text($text_view_header, " " . &ui_textbox("lines", $lines, 3), " $sel"),"\n";
|
print &text('view_header3', " $sel"),"\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print &text($text_view_header, " " . &ui_textbox("lines", $lines, 3), " $sel"),"\n";
|
||||||
|
}
|
||||||
print " \n";
|
print " \n";
|
||||||
print &text('view_filter', " " . &ui_textbox("filter", $in{'filter'}, 25)),"\n";
|
print &text('view_filter', " " . &ui_textbox("filter", $in{'filter'}, 12)),"\n";
|
||||||
|
|
||||||
print " \n";
|
print " \n";
|
||||||
print &ui_submit($text{'view_refresh'});
|
print &ui_submit($text{'view_filter_btn'});
|
||||||
print &ui_form_end(),"<br>\n";
|
print &ui_form_end(),"<br>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
logviewer/view_log_progress.cgi
Normal file
47
logviewer/view_log_progress.cgi
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/local/bin/perl
|
||||||
|
# view_log_progress.cgi
|
||||||
|
# Returns progressive output for some system log
|
||||||
|
|
||||||
|
require './logviewer-lib.pl';
|
||||||
|
&ReadParse();
|
||||||
|
&foreign_require("proc", "proc-lib.pl");
|
||||||
|
|
||||||
|
# Send headers
|
||||||
|
print "Content-Type: text/plain\n\n";
|
||||||
|
|
||||||
|
# System log to follow
|
||||||
|
my @systemctl_cmds = &get_systemctl_cmds(1);
|
||||||
|
my ($log) = grep { $_->{'id'} eq $in{'idx'} } @systemctl_cmds;
|
||||||
|
if (!&can_edit_log($log) ||
|
||||||
|
!$log->{'cmd'} ||
|
||||||
|
$log->{'cmd'} !~ /^journalctl/) {
|
||||||
|
print $text{'save_ecannot3'};
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable output buffering
|
||||||
|
$| = 1;
|
||||||
|
|
||||||
|
# No lines for real time logs
|
||||||
|
$log->{'cmd'} =~ s/\s+\-n\s+\d+//;
|
||||||
|
|
||||||
|
# Show real time logs
|
||||||
|
$log->{'cmd'} .= " -f";
|
||||||
|
|
||||||
|
# Add filter to the command if present
|
||||||
|
my $filter = $in{'filter'} ? quotemeta($in{'filter'}) : "";
|
||||||
|
if ($filter) {
|
||||||
|
$log->{'cmd'} .= " -g $filter";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open a pipe to the journalctl command
|
||||||
|
my $pid = open(my $fh, '-|', $log->{'cmd'}) ||
|
||||||
|
print &text('save_ecannot4', $log->{'cmd'}).": $!";
|
||||||
|
|
||||||
|
# Read and output the log
|
||||||
|
while (my $line = <$fh>) {
|
||||||
|
print $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up when done
|
||||||
|
close($fh);
|
||||||
@@ -16,7 +16,7 @@ fwd_mode=0
|
|||||||
delete_warn=y
|
delete_warn=y
|
||||||
top_buttons=2
|
top_buttons=2
|
||||||
view_html=2
|
view_html=2
|
||||||
view_images=3
|
view_images=1
|
||||||
mail_usermin=mail
|
mail_usermin=mail
|
||||||
sync_create=0
|
sync_create=0
|
||||||
sync_modify=1
|
sync_modify=1
|
||||||
|
|||||||
@@ -2947,7 +2947,8 @@ my $iframe_body = <<EOF;
|
|||||||
theme_mail_iframe_onload(iframe);
|
theme_mail_iframe_onload(iframe);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const iframe_spinner = document.querySelector('#mail-iframe-spinner'),
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document,
|
||||||
|
iframe_spinner = document.querySelector('#mail-iframe-spinner'),
|
||||||
iframe_resize = function() {
|
iframe_resize = function() {
|
||||||
const iframeobj = document.querySelector('#mail-iframe'),
|
const iframeobj = document.querySelector('#mail-iframe'),
|
||||||
iframe_height_bound = iframeobj.contentWindow.document.body.getBoundingClientRect().bottom,
|
iframe_height_bound = iframeobj.contentWindow.document.body.getBoundingClientRect().bottom,
|
||||||
@@ -2955,8 +2956,11 @@ my $iframe_body = <<EOF;
|
|||||||
iframe_height =
|
iframe_height =
|
||||||
iframe_height_bound > iframe_scroll_height ?
|
iframe_height_bound > iframe_scroll_height ?
|
||||||
iframe_height_bound : iframe_scroll_height;
|
iframe_height_bound : iframe_scroll_height;
|
||||||
iframeobj.style.height = Math.ceil(iframe_height) + "px";
|
iframeobj.style.height = Math.ceil(iframe_height - 1) + "px";
|
||||||
};
|
};
|
||||||
|
iframeDoc.body.style.removeProperty('width');
|
||||||
|
iframeDoc.body.style.margin = '4px';
|
||||||
|
iframeDoc.body.style.padding = '0';
|
||||||
iframe_spinner && iframe_spinner.remove();
|
iframe_spinner && iframe_spinner.remove();
|
||||||
iframe.classList.add("loaded");
|
iframe.classList.add("loaded");
|
||||||
setTimeout(iframe_resize);
|
setTimeout(iframe_resize);
|
||||||
@@ -2980,6 +2984,15 @@ my $iframe_body = <<EOF;
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
}, 99);
|
}, 99);
|
||||||
|
iframeDoc.addEventListener('click', function(event) {
|
||||||
|
if (event.target.tagName.toLowerCase() === 'summary' &&
|
||||||
|
event.target.dataset.resize === 'iframe') {
|
||||||
|
setTimeout(iframe_resize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
iframe.contentWindow.addEventListener('resize', function() {
|
||||||
|
setTimeout(iframe_resize);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<iframe
|
<iframe
|
||||||
@@ -4240,4 +4253,562 @@ foreach my $h (@{$mail->{'headers'}}) {
|
|||||||
return $rv;
|
return $rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# parse_calendar_file(calendar-file|lines)
|
||||||
|
# Parses an iCalendar file and returns a list of events
|
||||||
|
sub parse_calendar_file
|
||||||
|
{
|
||||||
|
my ($calendar_file) = @_;
|
||||||
|
my (@events, %event, $line);
|
||||||
|
eval "use DateTime; use DateTime::TimeZone;";
|
||||||
|
return \@events if ($@);
|
||||||
|
# Timezone map
|
||||||
|
my %timezone_map = (
|
||||||
|
'Afghanistan Time' => 'AFT',
|
||||||
|
'Alaskan Daylight Time' => 'AKDT',
|
||||||
|
'Alaskan Standard Time' => 'AKST',
|
||||||
|
'Anadyr Time' => 'ANAT',
|
||||||
|
'Arabian Standard Time' => 'AST',
|
||||||
|
'Argentina Time' => 'ART',
|
||||||
|
'Atlantic Daylight Time' => 'ADT',
|
||||||
|
'Atlantic Standard Time' => 'AST',
|
||||||
|
'Australian Central Daylight Time' => 'ACDT',
|
||||||
|
'Australian Central Standard Time' => 'ACST',
|
||||||
|
'Australian Eastern Daylight Time' => 'AEDT',
|
||||||
|
'Australian Eastern Standard Time' => 'AEST',
|
||||||
|
'Bangladesh Standard Time' => 'BST',
|
||||||
|
'Brasília Time' => 'BRT',
|
||||||
|
'British Summer Time' => 'BST',
|
||||||
|
'Central Africa Time' => 'CAT',
|
||||||
|
'Central Asia Time' => 'ALMT',
|
||||||
|
'Central Daylight Time' => 'CDT',
|
||||||
|
'Central Daylight Time (US)' => 'CDT',
|
||||||
|
'Central European Summer Time' => 'CEST',
|
||||||
|
'Central European Time' => 'CET',
|
||||||
|
'Central Indonesia Time' => 'WITA',
|
||||||
|
'Central Standard Time (Australia)' => 'CST',
|
||||||
|
'Central Standard Time (US)' => 'CST',
|
||||||
|
'Central Standard Time' => 'CST',
|
||||||
|
'Chamorro Daylight Time' => 'CHDT',
|
||||||
|
'Chamorro Standard Time' => 'CHST',
|
||||||
|
'China Standard Time' => 'CST',
|
||||||
|
'Coordinated Universal Time' => 'UTC',
|
||||||
|
'East Africa Time' => 'EAT',
|
||||||
|
'Eastern Africa Time' => 'EAT',
|
||||||
|
'Eastern Daylight Time' => 'EDT',
|
||||||
|
'Eastern Daylight Time (US)' => 'EDT',
|
||||||
|
'Eastern European Summer Time' => 'EEST',
|
||||||
|
'Eastern European Time' => 'EET',
|
||||||
|
'Eastern Indonesia Time' => 'WIT',
|
||||||
|
'Eastern Standard Time (Australia)' => 'EST',
|
||||||
|
'Eastern Standard Time (US)' => 'EST',
|
||||||
|
'Eastern Standard Time' => 'EST',
|
||||||
|
'Fiji Time' => 'FJT',
|
||||||
|
'Greenwich Mean Time' => 'GMT',
|
||||||
|
'Hawaii-Aleutian Daylight Time' => 'HADT',
|
||||||
|
'Hawaii-Aleutian Standard Time' => 'HAST',
|
||||||
|
'Hawaiian Standard Time' => 'HST',
|
||||||
|
'Hong Kong Time' => 'HKT',
|
||||||
|
'Indian Standard Time' => 'IST',
|
||||||
|
'Iran Standard Time' => 'IRST',
|
||||||
|
'Irish Standard Time' => 'IST',
|
||||||
|
'Israel Standard Time' => 'IST',
|
||||||
|
'Japan Standard Time' => 'JST',
|
||||||
|
'Korea Standard Time' => 'KST',
|
||||||
|
'Magadan Time' => 'MAGT',
|
||||||
|
'Malaysia Time' => 'MYT',
|
||||||
|
'Moscow Standard Time' => 'MSK',
|
||||||
|
'Mountain Daylight Time' => 'MDT',
|
||||||
|
'Mountain Standard Time' => 'MST',
|
||||||
|
'Myanmar Standard Time' => 'MMT',
|
||||||
|
'Nepal Time' => 'NPT',
|
||||||
|
'New Caledonia Time' => 'NCT',
|
||||||
|
'New Zealand Daylight Time' => 'NZDT',
|
||||||
|
'New Zealand Standard Time' => 'NZST',
|
||||||
|
'Newfoundland Daylight Time' => 'NDT',
|
||||||
|
'Newfoundland Standard Time' => 'NST',
|
||||||
|
'Pacific Daylight Time' => 'PDT',
|
||||||
|
'Pacific Standard Time' => 'PST',
|
||||||
|
'Pakistan Standard Time' => 'PKT',
|
||||||
|
'Philippine Time' => 'PHT',
|
||||||
|
'Sakhalin Time' => 'SAKT',
|
||||||
|
'Samoa Standard Time' => 'SST',
|
||||||
|
'Singapore Standard Time' => 'SGT',
|
||||||
|
'South Africa Standard Time' => 'SAST',
|
||||||
|
'Tahiti Time' => 'TAHT',
|
||||||
|
'Venezuelan Standard Time' => 'VET',
|
||||||
|
'West Africa Time' => 'WAT',
|
||||||
|
'Western European Summer Time' => 'WEST',
|
||||||
|
'Western European Time' => 'WET',
|
||||||
|
'Western Indonesia Time' => 'WIB',
|
||||||
|
'Western Standard Time (Australia)' => 'WST',
|
||||||
|
);
|
||||||
|
# Make a date from a special timestamp
|
||||||
|
my $adjust_time_with_timezone = sub {
|
||||||
|
my ($time, $tzid) = @_;
|
||||||
|
my $dt = DateTime->new(
|
||||||
|
year => substr($time, 0, 4),
|
||||||
|
month => substr($time, 4, 2),
|
||||||
|
day => substr($time, 6, 2),
|
||||||
|
hour => substr($time, 9, 2),
|
||||||
|
minute => substr($time, 11, 2),
|
||||||
|
second => substr($time, 13, 2),
|
||||||
|
time_zone => $tzid);
|
||||||
|
my $local_dt = $dt->clone->set_time_zone('local');
|
||||||
|
return {
|
||||||
|
formatted => $dt->strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
timestamp => $dt->epoch,
|
||||||
|
formatted_local => $local_dt->strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
timestamp_local => $local_dt->epoch,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Lines processor
|
||||||
|
my $process_line = sub
|
||||||
|
{
|
||||||
|
my ($line) = @_;
|
||||||
|
# Start a new event
|
||||||
|
if ($line =~ /^BEGIN:VEVENT/) {
|
||||||
|
%event = ();
|
||||||
|
$event{'description'} = [ ];
|
||||||
|
$event{'attendees'} = [ ];
|
||||||
|
}
|
||||||
|
# Convert times using the timezone
|
||||||
|
elsif ($line =~ /^END:VEVENT/) {
|
||||||
|
# Local timezone
|
||||||
|
$event{'tzid_local'} = DateTime::TimeZone->new(name => 'local')->name();
|
||||||
|
$event{'tzid'} = 'UTC', $event{'tzid_missing'} = 1 if (!$event{'tzid'});
|
||||||
|
# Adjust times with timezone
|
||||||
|
my ($adjusted_start, $adjusted_end);
|
||||||
|
$event{'tzid'} = $timezone_map{$event{'tzid'}} || $event{'tzid'};
|
||||||
|
# Add single start/end time
|
||||||
|
if ($event{'dtstart'}) {
|
||||||
|
$adjusted_start =
|
||||||
|
$adjust_time_with_timezone->($event{'dtstart'},
|
||||||
|
$event{'tzid'});
|
||||||
|
$event{'dtstart_timestamp'} = $adjusted_start->{'timestamp'};
|
||||||
|
my $dtstart_date =
|
||||||
|
&make_date($event{'dtstart_timestamp'},
|
||||||
|
{ tz => $event{'tzid'} });
|
||||||
|
$event{'dtstart_date'} =
|
||||||
|
"$dtstart_date->{'short'} $dtstart_date->{'timeshort'}";
|
||||||
|
$event{'dtstart_local_timestamp'} =
|
||||||
|
$adjusted_start->{'timestamp_local'};
|
||||||
|
$event{'dtstart_local_date'} =
|
||||||
|
&make_date($event{'dtstart_local_timestamp'});
|
||||||
|
}
|
||||||
|
if ($event{'dtend'}) {
|
||||||
|
$adjusted_end =
|
||||||
|
$adjust_time_with_timezone->($event{'dtend'}, $event{'tzid'});
|
||||||
|
$event{'dtend_timestamp'} = $adjusted_end->{'timestamp'};
|
||||||
|
my $dtend_date = &make_date($event{'dtend_timestamp'},
|
||||||
|
{ tz => $event{'tzid'} });
|
||||||
|
$event{'dtend_date'} =
|
||||||
|
"$dtend_date->{'short'} $dtend_date->{'timeshort'}";
|
||||||
|
$event{'dtend_local_timestamp'} =
|
||||||
|
$adjusted_end->{'timestamp_local'};
|
||||||
|
$event{'dtend_local_date'} =
|
||||||
|
&make_date($event{'dtend_local_timestamp'});
|
||||||
|
}
|
||||||
|
if ($event{'dtstart'} && $event{'dtend'}) {
|
||||||
|
# Try to add local 'when (period)'
|
||||||
|
my $dtstart_local_obj =
|
||||||
|
$event{'_obj_dtstart_local_time'} =
|
||||||
|
make_date($event{'dtstart_local_timestamp'}, { });
|
||||||
|
my $dtend_local_obj =
|
||||||
|
$event{'_obj_dtend_local_time'} =
|
||||||
|
make_date($event{'dtend_local_timestamp'}, { });
|
||||||
|
# Build when local, e.g.:
|
||||||
|
# Tue Jun 04, 2024 04:30 PM – 05:15
|
||||||
|
# PM (Asia/Nicosia +0300)
|
||||||
|
# or
|
||||||
|
# Tue Jun 04, 2024 04:30 PM – Wed Jun 05, 2024 01:15
|
||||||
|
# AM (Asia/Nicosia +0300)
|
||||||
|
$event{'dtwhen_local'} =
|
||||||
|
# Start local
|
||||||
|
$dtstart_local_obj->{'week'}.' '.
|
||||||
|
$dtstart_local_obj->{'month'}.' '.
|
||||||
|
$dtstart_local_obj->{'day'}.', '.
|
||||||
|
$dtstart_local_obj->{'year'}.' '.
|
||||||
|
$dtstart_local_obj->{'timeshort'}.' – ';
|
||||||
|
# End local
|
||||||
|
if ($dtstart_local_obj->{'year'} eq
|
||||||
|
$dtend_local_obj->{'year'} &&
|
||||||
|
$dtstart_local_obj->{'month'} eq
|
||||||
|
$dtend_local_obj->{'month'} &&
|
||||||
|
$dtstart_local_obj->{'day'} eq
|
||||||
|
$dtend_local_obj->{'day'}) {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
$dtend_local_obj->{'timeshort'};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
$dtend_local_obj->{'week'}.' '.
|
||||||
|
$dtend_local_obj->{'month'}.' '.
|
||||||
|
$dtend_local_obj->{'day'}.', '.
|
||||||
|
$dtend_local_obj->{'year'}.' '.
|
||||||
|
$dtend_local_obj->{'timeshort'};
|
||||||
|
}
|
||||||
|
# Timezone local
|
||||||
|
if ($event{'tzid_local'} ||
|
||||||
|
$dtstart_local_obj->{'tz'}) {
|
||||||
|
if ($event{'tzid_local'} &&
|
||||||
|
$dtstart_local_obj->{'tz'}) {
|
||||||
|
if ($event{'tzid_local'} eq
|
||||||
|
$dtstart_local_obj->{'tz'}) {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
" ($event{'tzid_local'})";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
" ($event{'tzid_local'} ".
|
||||||
|
"$dtstart_local_obj->{'tz'})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elsif ($event{'tzid_local'}) {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
" ($event{'tzid_local'})";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$event{'dtwhen_local'} .=
|
||||||
|
" ($dtstart_local_obj->{'tz'})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Try to add original 'when (period)'
|
||||||
|
my $dtstart_obj =
|
||||||
|
$event{'_obj_dtstart_time'} =
|
||||||
|
make_date($event{'dtstart_timestamp'},
|
||||||
|
{ tz => $event{'tzid'} });
|
||||||
|
my $dtend_obj =
|
||||||
|
$event{'_obj_dtend_time'} =
|
||||||
|
make_date($event{'dtend_timestamp'},
|
||||||
|
{ tz => $event{'tzid'} });
|
||||||
|
# Build original when
|
||||||
|
if (!$event{'tzid_missing'}) {
|
||||||
|
$event{'dtwhen'} =
|
||||||
|
# Start original
|
||||||
|
$dtstart_obj->{'week'}.' '.
|
||||||
|
$dtstart_obj->{'month'}.' '.
|
||||||
|
$dtstart_obj->{'day'}.', '.
|
||||||
|
$dtstart_obj->{'year'}.' '.
|
||||||
|
$dtstart_obj->{'timeshort'}.' – ';
|
||||||
|
# End original
|
||||||
|
if ($dtstart_obj->{'year'} eq
|
||||||
|
$dtend_obj->{'year'} &&
|
||||||
|
$dtstart_obj->{'month'} eq
|
||||||
|
$dtend_obj->{'month'} &&
|
||||||
|
$dtstart_obj->{'day'} eq
|
||||||
|
$dtend_obj->{'day'}) {
|
||||||
|
$event{'dtwhen'} .=
|
||||||
|
$dtend_obj->{'timeshort'};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$event{'dtwhen'} .=
|
||||||
|
$dtend_obj->{'week'}.' '.
|
||||||
|
$dtend_obj->{'month'}.' '.
|
||||||
|
$dtend_obj->{'day'}.', '.
|
||||||
|
$dtend_obj->{'year'}.' '.
|
||||||
|
$dtend_obj->{'timeshort'};
|
||||||
|
}
|
||||||
|
# Timezone original
|
||||||
|
if ($dtstart_obj->{'tz'}) {
|
||||||
|
$event{'dtwhen'} .=
|
||||||
|
" ($dtstart_obj->{'tz'})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Add the event to the list
|
||||||
|
push(@events, { %event });
|
||||||
|
}
|
||||||
|
# Parse fields
|
||||||
|
elsif ($line =~ /^SUMMARY.*?:(.*)$/) {
|
||||||
|
$event{'summary'} = $1;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DTSTART:(.*)$/) {
|
||||||
|
$event{'dtstart'} = $1;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DTSTART;TZID=(.*?):(.*)$/) {
|
||||||
|
$event{'tzid'} = $1;
|
||||||
|
$event{'dtstart'} = $2;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DTEND:(.*)$/) {
|
||||||
|
$event{'dtend'} = $1;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DTEND;TZID=(.*?):(.*)$/) {
|
||||||
|
$event{'tzid'} = $1;
|
||||||
|
$event{'dtend'} = $2;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DESCRIPTION:(.*)$/) {
|
||||||
|
my $description = $1;
|
||||||
|
$description =~ s/\\n/<br>/g;
|
||||||
|
$description =~ s/\\//g;
|
||||||
|
unshift(@{$event{'description'}}, $description);
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^DESCRIPTION;LANGUAGE=([a-z]{2}-[A-Z]{2}):(.*)$/) {
|
||||||
|
my $description = $2;
|
||||||
|
$description =~ s/\\n/<br>/g;
|
||||||
|
$description =~ s/\\//g;
|
||||||
|
unshift(@{$event{'description'}}, $description);
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^LOCATION.*?:(.*)$/) {
|
||||||
|
$event{'location'} = $1;
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=(.*?):mailto:(.*)$/ ||
|
||||||
|
$line =~ /^ATTENDEE;.*CN=(.*?);.*mailto:(.*)$/ ||
|
||||||
|
$line =~ /^ATTENDEE:mailto:(.*)$/) {
|
||||||
|
push(@{$event{'attendees'}}, { 'name' => $1, 'email' => $2 });
|
||||||
|
}
|
||||||
|
elsif ($line =~ /^ORGANIZER;CN=(.*?):(?:mailto:)?(.*)$/) {
|
||||||
|
$event{'organizer_name'} = $1;
|
||||||
|
$event{'organizer_email'} = $2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
# Read the ICS file lines or just use the lines
|
||||||
|
my $ics_file_lines =
|
||||||
|
-r $calendar_file ?
|
||||||
|
&read_file_lines($calendar_file, 1) :
|
||||||
|
[ split(/\r?\n/, $calendar_file) ];
|
||||||
|
# Process each line of the ICS file
|
||||||
|
foreach my $ics_file_line (@$ics_file_lines) {
|
||||||
|
# Check if the line is a continuation of the previous line
|
||||||
|
if ($ics_file_line =~ /^[ \t](.*)$/) {
|
||||||
|
$line .= $1; # Concatenate with the previous line
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Process the previous line
|
||||||
|
$process_line->($line) if ($line);
|
||||||
|
$line = $ics_file_line; # Start a new line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Process the last line
|
||||||
|
$process_line->($line) if ($line);
|
||||||
|
# Return the list of events
|
||||||
|
return \@events;
|
||||||
|
}
|
||||||
|
|
||||||
|
# get_calendar_data(&calendars)
|
||||||
|
# Returns HTML for all parsed calendars
|
||||||
|
sub get_calendar_data
|
||||||
|
{
|
||||||
|
my ($calendars) = @_;
|
||||||
|
my @calendars = @{$calendars};
|
||||||
|
$calendars = { };
|
||||||
|
if (@calendars) {
|
||||||
|
# Fonts for our HTML
|
||||||
|
$calendars->{'html'} .= &theme_fonts()
|
||||||
|
if (defined(&theme_fonts));
|
||||||
|
my $theme_css_inline;
|
||||||
|
$theme_css_inline = &theme_css_inline('calendar')
|
||||||
|
if (defined(&theme_css_inline));
|
||||||
|
# CSS for HTML version
|
||||||
|
$calendars->{'html'} .= <<STYLE;
|
||||||
|
<style>
|
||||||
|
.calendar-table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid #99999933;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-family: 'RobotoLocal',arial,helvetica,clean,sans-serif !important;
|
||||||
|
}
|
||||||
|
.calendar-table-inner {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
.calendar-table td {
|
||||||
|
padding: 5px;
|
||||||
|
vertical-align: top;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.calendar-table .calendar-cell {
|
||||||
|
background-color: #99999916;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
|
padding: 2px;
|
||||||
|
padding-top: 24px;
|
||||||
|
padding-bottom: 24px;
|
||||||
|
width: 100px;
|
||||||
|
min-width: 100px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.calendar-month {
|
||||||
|
font-size: 19px;
|
||||||
|
color: #1d72ff;
|
||||||
|
text-align: center;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
|
.calendar-day {
|
||||||
|
font-size: 19px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
.calendar-week {
|
||||||
|
font-size: 13px;
|
||||||
|
border-top: 1px dotted #999999aa;
|
||||||
|
padding: 6px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.calendar-details h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.calendar-details p {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
.calendar-details .title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.calendar-details .detail strong {
|
||||||
|
opacity: 0.83;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.calendar-details .detail + .desc p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
details.calendar-details {
|
||||||
|
font-size: 87%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
.calendar-details > .calendar-table-inner .detail:has(strong),
|
||||||
|
.calendar-details > .calendar-table-inner .detail strong,
|
||||||
|
.calendar-details > .calendar-table-inner .detail + td {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
details.calendar-details summary {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
details.calendar-details tr:has(>.detail+td:empty),
|
||||||
|
.calendar-details tr:has(>.detail+td:empty) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
$theme_css_inline
|
||||||
|
</style>
|
||||||
|
STYLE
|
||||||
|
foreach my $calendar (@calendars) {
|
||||||
|
my $title = $calendar->{'summary'} || $calendar->{'description'};
|
||||||
|
my $orginizer = $calendar->{'organizer_name'};
|
||||||
|
my @attendees;
|
||||||
|
foreach my $a (@{$calendar->{'attendees'}}) {
|
||||||
|
push(@attendees, { name => $a->{'name'},
|
||||||
|
email => $a->{'email'} });
|
||||||
|
}
|
||||||
|
my $who = join(", ", map { $_->{'name'} } @attendees);
|
||||||
|
if ($who && $orginizer) {
|
||||||
|
$who .= ", ${orginizer}*";
|
||||||
|
}
|
||||||
|
elsif ($orginizer) {
|
||||||
|
$who = "${orginizer}*";
|
||||||
|
}
|
||||||
|
# HTML version
|
||||||
|
$calendars->{'html'} .= <<HTML;
|
||||||
|
<table class="calendar-table">
|
||||||
|
<tr>
|
||||||
|
<td class="calendar-cell">
|
||||||
|
<div class="calendar-block">
|
||||||
|
<div class="calendar-month">
|
||||||
|
$calendar->{'_obj_dtstart_local_time'}->{'month'}
|
||||||
|
</div>
|
||||||
|
<div class="calendar-day">
|
||||||
|
$calendar->{'_obj_dtstart_local_time'}->{'day'}
|
||||||
|
</div>
|
||||||
|
<div class="calendar-week">
|
||||||
|
$calendar->{'_obj_dtstart_local_time'}->{'week'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="calendar-details">
|
||||||
|
<table class="calendar-table-inner">
|
||||||
|
<tr>
|
||||||
|
<td class="title" colspan="2">
|
||||||
|
<strong>$title</strong>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_when'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$calendar->{'dtwhen_local'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_where'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$calendar->{'location'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_who'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$who</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<details class="calendar-details">
|
||||||
|
<summary data-resize="iframe"></summary>
|
||||||
|
<table class="calendar-table-inner">
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_orginizertime'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$calendar->{'dtwhen'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_orginizername'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$calendar->{'organizer_name'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_orginizeremail'}</strong>
|
||||||
|
</td>
|
||||||
|
<td>$calendar->{'organizer_email'}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_attendees'}</strong>
|
||||||
|
</td>
|
||||||
|
<td class="desc">@{[join('', map {
|
||||||
|
"<p>$_->{'name'}<br>$_->{'email'}</p>"
|
||||||
|
} @attendees)]}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="detail">
|
||||||
|
<strong>$text{'view_ical_desc'}</strong>
|
||||||
|
</td>
|
||||||
|
<td class="desc">@{[join('<br>',
|
||||||
|
@{$calendar->{'description'}})]}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</details>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
HTML
|
||||||
|
# Text version
|
||||||
|
my %textical = (
|
||||||
|
'view_ical' => $title,
|
||||||
|
'view_ical_when' => $calendar->{'dtwhen_local'},
|
||||||
|
'view_ical_where' => $calendar->{'location'},
|
||||||
|
'view_ical_who' => $who
|
||||||
|
);
|
||||||
|
my $max_label_length = 0;
|
||||||
|
foreach my $key (sort keys %textical) {
|
||||||
|
my $label_length = length($text{$key});
|
||||||
|
if ($label_length > $max_label_length) {
|
||||||
|
$max_label_length = $label_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$calendars->{'text'} = "=" x 79 . "\n";
|
||||||
|
foreach my $key (sort keys %textical) {
|
||||||
|
my $label = $text{$key};
|
||||||
|
my $value = $textical{$key};
|
||||||
|
my $spaces .= " " x ($max_label_length - length($label));
|
||||||
|
$calendars->{'text'} .= "$label$spaces : $value\n";
|
||||||
|
}
|
||||||
|
$calendars->{'text'} .= "=" x 79 . "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $calendars;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -154,6 +154,15 @@ view_sub=Attached Email
|
|||||||
view_sub2=Attached email from $1
|
view_sub2=Attached email from $1
|
||||||
view_egone=This message no longer exists
|
view_egone=This message no longer exists
|
||||||
view_eugone=This user does not exist
|
view_eugone=This user does not exist
|
||||||
|
view_ical=Event
|
||||||
|
view_ical_when=When
|
||||||
|
view_ical_where=Where
|
||||||
|
view_ical_who=Who
|
||||||
|
view_ical_orginizertime=Organizer time
|
||||||
|
view_ical_orginizername=Organizer name
|
||||||
|
view_ical_orginizeremail=Organizer email
|
||||||
|
view_ical_attendees=Attendees details
|
||||||
|
view_ical_desc=Event description
|
||||||
|
|
||||||
view_gnupg=GnuPG signature verification
|
view_gnupg=GnuPG signature verification
|
||||||
view_gnupg_0=Signature by $1 is valid.
|
view_gnupg_0=Signature by $1 is valid.
|
||||||
@@ -323,6 +332,7 @@ folder_inbox=Inbox
|
|||||||
folder_sent=Sent
|
folder_sent=Sent
|
||||||
folder_drafts=Drafts
|
folder_drafts=Drafts
|
||||||
folder_trash=Trash
|
folder_trash=Trash
|
||||||
|
folder_spam=Spam
|
||||||
|
|
||||||
detach_err=Failed to detach file
|
detach_err=Failed to detach file
|
||||||
detach_edir=No file or directory to save to entered
|
detach_edir=No file or directory to save to entered
|
||||||
|
|||||||
@@ -1287,4 +1287,3 @@ return $rv;
|
|||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,16 @@ foreach $s (@sub) {
|
|||||||
@attach = grep { $_ ne $body && $_ ne $dstatus } @attach;
|
@attach = grep { $_ ne $body && $_ ne $dstatus } @attach;
|
||||||
@attach = grep { !$_->{'attach'} } @attach;
|
@attach = grep { !$_->{'attach'} } @attach;
|
||||||
|
|
||||||
|
# Calendar attachments
|
||||||
|
my @calendars;
|
||||||
|
eval {
|
||||||
|
foreach my $i (grep { $_->{'data'} }
|
||||||
|
grep { $_->{'type'} =~ /^text\/calendar/ } @attach) {
|
||||||
|
my $calendars = &parse_calendar_file($i->{'data'});
|
||||||
|
push(@calendars, @{$calendars});
|
||||||
|
}};
|
||||||
|
|
||||||
|
# Mail buttons
|
||||||
if ($config{'top_buttons'} == 2 && &editable_mail($mail)) {
|
if ($config{'top_buttons'} == 2 && &editable_mail($mail)) {
|
||||||
&show_mail_buttons(1, scalar(@sub));
|
&show_mail_buttons(1, scalar(@sub));
|
||||||
print "<p class='mail_buttons_divide'></p>\n";
|
print "<p class='mail_buttons_divide'></p>\n";
|
||||||
@@ -138,11 +148,15 @@ else {
|
|||||||
print &ui_table_end();
|
print &ui_table_end();
|
||||||
|
|
||||||
# Show body attachment, with properly linked URLs
|
# Show body attachment, with properly linked URLs
|
||||||
@bodyright = ( );
|
my $bodycontents;
|
||||||
|
my @bodyright = ( );
|
||||||
|
my $calendars = &get_calendar_data(\@calendars);
|
||||||
if ($body && $body->{'data'} =~ /\S/) {
|
if ($body && $body->{'data'} =~ /\S/) {
|
||||||
if ($body eq $textbody) {
|
if ($body eq $textbody) {
|
||||||
# Show plain text
|
# Show plain text
|
||||||
$bodycontents = "<pre>";
|
$bodycontents = "<pre>";
|
||||||
|
$bodycontents .= $calendars->{'text'}
|
||||||
|
if ($calendars->{'text'});
|
||||||
foreach $l (&wrap_lines(&eucconv($body->{'data'}),
|
foreach $l (&wrap_lines(&eucconv($body->{'data'}),
|
||||||
$config{'wrap_width'})) {
|
$config{'wrap_width'})) {
|
||||||
$bodycontents .= &link_urls_and_escape($l,
|
$bodycontents .= &link_urls_and_escape($l,
|
||||||
@@ -156,7 +170,9 @@ if ($body && $body->{'data'} =~ /\S/) {
|
|||||||
}
|
}
|
||||||
elsif ($body eq $htmlbody) {
|
elsif ($body eq $htmlbody) {
|
||||||
# Attempt to show HTML
|
# Attempt to show HTML
|
||||||
$bodycontents = $body->{'data'};
|
$bodycontents = $calendars->{'html'}
|
||||||
|
if ($calendars->{'html'});
|
||||||
|
$bodycontents .= $body->{'data'};
|
||||||
my @imageurls;
|
my @imageurls;
|
||||||
my $image_mode = int(defined($in{'images'}) ? $in{'images'} : $config{'view_images'});
|
my $image_mode = int(defined($in{'images'}) ? $in{'images'} : $config{'view_images'});
|
||||||
$bodycontents = &disable_html_images($bodycontents, $image_mode, \@imageurls);
|
$bodycontents = &disable_html_images($bodycontents, $image_mode, \@imageurls);
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ Corvus Latinux 8.0 redhat-linux 7.1 `cat /etc/latinux-release 2>/dev/null` =~
|
|||||||
Immunix Linux $1 redhat-linux $1 $etc_issue =~ /Immunix.*\s([0-9\.]+)/i || `cat /etc/immunix-release 2>/dev/null` =~ /([0-9\.]+)/
|
Immunix Linux $1 redhat-linux $1 $etc_issue =~ /Immunix.*\s([0-9\.]+)/i || `cat /etc/immunix-release 2>/dev/null` =~ /([0-9\.]+)/
|
||||||
|
|
||||||
# All versions of Gentoo (which don't appear to have version numbers)
|
# All versions of Gentoo (which don't appear to have version numbers)
|
||||||
Gentoo Linux Any version gentoo-linux * -d "/usr/portage"
|
Gentoo Linux Any version gentoo-linux * -d "/usr/portage" || `cat /etc/os-release 2>/dev/null` =~ /gentoo/
|
||||||
|
|
||||||
# Secure Linux (now called Trustix?)
|
# Secure Linux (now called Trustix?)
|
||||||
Secure Linux 1.0 redhat-linux 7.2 `cat /etc/securelinux-release 2>/dev/null` =~ /SecureLinux.*1\.0/i
|
Secure Linux 1.0 redhat-linux 7.2 `cat /etc/securelinux-release 2>/dev/null` =~ /SecureLinux.*1\.0/i
|
||||||
|
|||||||
@@ -86,10 +86,11 @@ print &ui_table_start($text{'general_title_others'}, "width=100%", 4);
|
|||||||
|
|
||||||
&option_radios_freefield("mydomain", 40, $text{'opts_mydomain_default'});
|
&option_radios_freefield("mydomain", 40, $text{'opts_mydomain_default'});
|
||||||
|
|
||||||
&option_radios_freefield("mynetworks", 60, $text{'opts_mynetworks_default'});
|
&option_radios_freefield("mynetworks", 60, $text{'default'});
|
||||||
|
|
||||||
&option_select("mynetworks_style",
|
&option_select("mynetworks_style",
|
||||||
[ [ "subnet", $text{'opts_mynetworks_subnet'} ],
|
[ [ "", $text{'default'} ],
|
||||||
|
[ "subnet", $text{'opts_mynetworks_subnet'} ],
|
||||||
[ "class", $text{'opts_mynetworks_class'} ],
|
[ "class", $text{'opts_mynetworks_class'} ],
|
||||||
[ "host", $text{'opts_mynetworks_host'} ] ]);
|
[ "host", $text{'opts_mynetworks_host'} ] ]);
|
||||||
|
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ opts_mydomain_default=Default (provided by system)
|
|||||||
opts_myhostname=Internet hostname of this mail system
|
opts_myhostname=Internet hostname of this mail system
|
||||||
opts_myhostname_default=Default (provided by system)
|
opts_myhostname_default=Default (provided by system)
|
||||||
opts_mynetworks=Local networks
|
opts_mynetworks=Local networks
|
||||||
opts_mynetworks_default=Default (all attached networks)
|
|
||||||
opts_mynetworks_style=Automatic local networks
|
opts_mynetworks_style=Automatic local networks
|
||||||
opts_mynetworks_subnet=Same IP subnet
|
opts_mynetworks_subnet=Same IP subnet
|
||||||
opts_mynetworks_class=Same network class
|
opts_mynetworks_class=Same network class
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ if ($site{'size'} != $st[7] || !$site{'version'} || !$site{'fullversion'}) {
|
|||||||
|
|
||||||
# Get the list of modules
|
# Get the list of modules
|
||||||
local @mods;
|
local @mods;
|
||||||
open(MODS, "$config{'proftpd_path'} -l |");
|
open(MODS, "$config{'proftpd_path'} -vv |");
|
||||||
while(<MODS>) {
|
while(<MODS>) {
|
||||||
s/\r|\n//g;
|
s/\r|\n//g;
|
||||||
if (/^\s*(\S+)\.c$/) {
|
if (/^\s*(?<mod_built_in>\S+)\.c$|\s*(?<mod_loaded>mod_[a-zA-Z0-9_]+)\//) {
|
||||||
push(@mods, $1);
|
push(@mods, $+{mod_loaded} || $+{mod_built_in});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(MODS);
|
close(MODS);
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ $ver = &get_syslog_ng_version();
|
|||||||
my $index_econf2;
|
my $index_econf2;
|
||||||
if (&has_command('systemctl')) {
|
if (&has_command('systemctl')) {
|
||||||
if (&foreign_available('logviewer')) {
|
if (&foreign_available('logviewer')) {
|
||||||
$index_econf2 = &text('index_econf2', "System Logs Viewer", "@{[&get_webprefix()]}/logviewer") . "<p><br>";
|
my %logviewer_text = &load_language('logviewer');
|
||||||
|
$index_econf2 = &text('index_econf2',
|
||||||
|
$logviewer_text{'index_title'},
|
||||||
|
"@{[&get_webprefix()]}/logviewer") . "<p><br>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$ver) {
|
if (!$ver) {
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ if (!-r $config{'syslog_conf'}) {
|
|||||||
my $index_econf2;
|
my $index_econf2;
|
||||||
if (&has_command('systemctl')) {
|
if (&has_command('systemctl')) {
|
||||||
if (&foreign_available('logviewer')) {
|
if (&foreign_available('logviewer')) {
|
||||||
$index_econf2 = &text('index_econf2', "System Logs Viewer", "@{[&get_webprefix()]}/logviewer") . "<p><br>";
|
my %logviewer_text = &load_language('logviewer');
|
||||||
|
$index_econf2 = &text('index_econf2',
|
||||||
|
$logviewer_text{'index_title'},
|
||||||
|
"@{[&get_webprefix()]}/logviewer") . "<p><br>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Not installed (maybe using syslog-ng)
|
# Not installed (maybe using syslog-ng)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
index_title=System Logs
|
index_title=System Logs RS
|
||||||
index_m4msg=Your system log configuration file $1 appears to contain <tt>m4</tt> directives. Before it can be edited, Webmin needs to pass the file through <tt>m4</tt> to safely remove these directives.
|
index_m4msg=Your system log configuration file $1 appears to contain <tt>m4</tt> directives. Before it can be edited, Webmin needs to pass the file through <tt>m4</tt> to safely remove these directives.
|
||||||
index_m4=Remove m4 directives from config file
|
index_m4=Remove m4 directives from config file
|
||||||
index_econf=The syslog configuration file $1 was not found on your system. Maybe syslog is not installed, or a newer version like syslog-ng is in use, or the <a href='$2'>module configuration</a> is incorrect.
|
index_econf=The syslog configuration file $1 was not found on your system. Maybe syslog is not installed, or a newer version like syslog-ng is in use, or the <a href='$2'>module configuration</a> is incorrect.
|
||||||
@@ -16,7 +16,7 @@ index_cmd=Output from $1
|
|||||||
index_all=All users
|
index_all=All users
|
||||||
index_users=Users $1
|
index_users=Users $1
|
||||||
index_add=Add a new system log.
|
index_add=Add a new system log.
|
||||||
index_return=system logs
|
index_return=module index
|
||||||
index_restart=Apply Changes
|
index_restart=Apply Changes
|
||||||
index_restartmsg=Click this button to make the current configuration active by killing the running <tt>syslog</tt> process and restarting it.
|
index_restartmsg=Click this button to make the current configuration active by killing the running <tt>syslog</tt> process and restarting it.
|
||||||
index_start=Start Syslog Server
|
index_start=Start Syslog Server
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name=Syslog
|
name=Syslog
|
||||||
category=system
|
category=system
|
||||||
os_support=solaris *-linux freebsd openbsd macos hpux irix unixware aix netbsd openserver
|
os_support=solaris *-linux freebsd openbsd macos hpux irix unixware aix netbsd openserver
|
||||||
desc=System Logs
|
desc=System Logs RS
|
||||||
depends=proc
|
depends=proc
|
||||||
longdesc=Configure the syslog server on your system and view its log files.
|
longdesc=Configure the syslog server on your system and view its log files.
|
||||||
readonly=1
|
readonly=1
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ no warnings 'redefine';
|
|||||||
no warnings 'uninitialized';
|
no warnings 'uninitialized';
|
||||||
package system_status;
|
package system_status;
|
||||||
require './system-status-lib.pl';
|
require './system-status-lib.pl';
|
||||||
&scheduled_collect_system_info();
|
|
||||||
|
|
||||||
|
&scheduled_collect_system_info();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ if ($in{'action'} eq $text{'action_sync'}) {
|
|||||||
if (defined($in{'sync_service_name'}) &&
|
if (defined($in{'sync_service_name'}) &&
|
||||||
defined($in{'sync_service_status'})) {
|
defined($in{'sync_service_status'})) {
|
||||||
my $service_name = $in{'sync_service_name'};
|
my $service_name = $in{'sync_service_name'};
|
||||||
if ($service_name !~ /^(chronyd|systemd-timesyncd)$/) {
|
if ($service_name !~ /^(chronyd|chrony|systemd-timesyncd)$/) {
|
||||||
&error(&text('error_serviceunknown', &html_escape($service_name)));
|
&error(&text('error_serviceunknown', &html_escape($service_name)));
|
||||||
}
|
}
|
||||||
my $service_status = int($in{'sync_service_status'});
|
my $service_status = int($in{'sync_service_status'});
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use warnings;
|
|||||||
no warnings 'redefine';
|
no warnings 'redefine';
|
||||||
no warnings 'uninitialized';
|
no warnings 'uninitialized';
|
||||||
require './time-lib.pl';
|
require './time-lib.pl';
|
||||||
our (%in, %text, %config, %access, $base_remote_user, $get_hardware_time_error);
|
our (%in, %text, %config, %access, $base_remote_user,
|
||||||
|
$get_hardware_time_error, $cronyd_name);
|
||||||
|
|
||||||
my ($rawdate, $rawhwdate, %system_date, $rawtime, %hw_date, $txt);
|
my ($rawdate, $rawhwdate, %system_date, $rawtime, %hw_date, $txt);
|
||||||
$txt = "";
|
$txt = "";
|
||||||
@@ -174,13 +175,14 @@ if ( ( !$access{ 'sysdate' } && &has_command( "date" ) || !$access{ 'hwdate' } &
|
|||||||
$config{'timeserver_hardware'}));
|
$config{'timeserver_hardware'}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (&foreign_require('init') && &init::action_status('chronyd') > 0 && &has_command("chronyc")) {
|
if (&foreign_require('init') &&
|
||||||
my $chronyd_running = &init::status_action('chronyd');
|
&init::action_status($cronyd_name) > 0 && &has_command("chronyc")) {
|
||||||
my $chronyd_run_atboot = &init::action_status('chronyd');
|
my $chronyd_running = &init::status_action($cronyd_name);
|
||||||
my $ui_hiddens = &ui_hidden("sync_service_name", "chronyd");
|
my $chronyd_run_atboot = &init::action_status($cronyd_name);
|
||||||
|
my $ui_hiddens = &ui_hidden("sync_service_name", $cronyd_name);
|
||||||
$ui_hiddens .= &ui_hidden("timeserver", $config{'timeserver'})
|
$ui_hiddens .= &ui_hidden("timeserver", $config{'timeserver'})
|
||||||
if (!$ntp_support);
|
if (!$ntp_support);
|
||||||
print &ui_table_row(&text('index_tabsync2', 'chronyd'),
|
print &ui_table_row(&text('index_tabsync2', $cronyd_name),
|
||||||
$ui_hiddens.
|
$ui_hiddens.
|
||||||
&ui_radio("sync_service_status",
|
&ui_radio("sync_service_status",
|
||||||
(($chronyd_run_atboot == 2 && $chronyd_running) ? 2 :
|
(($chronyd_run_atboot == 2 && $chronyd_running) ? 2 :
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ our ($timezones_file, $currentzone_link, $currentzone_file, $timezones_dir,
|
|||||||
$sysclock_file);
|
$sysclock_file);
|
||||||
our ($get_hardware_time_error);
|
our ($get_hardware_time_error);
|
||||||
our $cron_cmd = "$module_config_directory/sync.pl";
|
our $cron_cmd = "$module_config_directory/sync.pl";
|
||||||
|
our $cronyd_name = $gconfig{'os_type'} eq 'debian-linux' ? 'chrony' : 'chronyd';
|
||||||
our $rawtime;
|
our $rawtime;
|
||||||
if ($config{'zone_style'}) {
|
if ($config{'zone_style'}) {
|
||||||
do "$config{'zone_style'}-lib.pl";
|
do "$config{'zone_style'}-lib.pl";
|
||||||
@@ -49,13 +50,13 @@ if (&has_command("ntpdate")) {
|
|||||||
elsif (&has_command("sntp")) {
|
elsif (&has_command("sntp")) {
|
||||||
$out = &backquote_logged("sntp -s $servs 2>&1");
|
$out = &backquote_logged("sntp -s $servs 2>&1");
|
||||||
}
|
}
|
||||||
elsif (&foreign_require('init') && &init::action_status('chronyd') > 0 && &has_command("chronyc")) {
|
elsif (&foreign_require('init') && &init::action_status($cronyd_name) > 0 && &has_command("chronyc")) {
|
||||||
my $chronyd_running = &init::status_action('chronyd');
|
my $chronyd_running = &init::status_action($cronyd_name);
|
||||||
$out = &backquote_logged("systemctl restart chronyd 2>&1");
|
$out = &backquote_logged("systemctl restart $cronyd_name 2>&1");
|
||||||
$out .= &backquote_logged("chronyc makestep 2>&1");
|
$out .= &backquote_logged("chronyc makestep 2>&1");
|
||||||
sleep ($chronyd_running ? 5 : 15);
|
sleep ($chronyd_running ? 5 : 15);
|
||||||
if (!$chronyd_running) {
|
if (!$chronyd_running) {
|
||||||
&backquote_logged("systemctl stop chronyd 2>&1");
|
&backquote_logged("systemctl stop $cronyd_name 2>&1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif (&foreign_require('init') && &init::action_status('systemd-timesyncd') > 0) {
|
elsif (&foreign_require('init') && &init::action_status('systemd-timesyncd') > 0) {
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ return 0;
|
|||||||
sub open_last_command
|
sub open_last_command
|
||||||
{
|
{
|
||||||
local ($fh, $user) = @_;
|
local ($fh, $user) = @_;
|
||||||
open($fh, "last $user |");
|
local $quser = quotemeta($user);
|
||||||
|
open($fh, "(last -w $quser || last $quser) |");
|
||||||
}
|
}
|
||||||
|
|
||||||
# read_last_line(handle)
|
# read_last_line(handle)
|
||||||
@@ -34,7 +35,9 @@ while(1) {
|
|||||||
chop($line = <$fh>);
|
chop($line = <$fh>);
|
||||||
if (!$line) { return (); }
|
if (!$line) { return (); }
|
||||||
if ($line =~ /^(reboot|shutdown)/) { next; }
|
if ($line =~ /^(reboot|shutdown)/) { next; }
|
||||||
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((\d+:\d+)\)/) {
|
if ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+\-\s+(\S+)\s+\((.*?\d+:\d+.*?)\)/) {
|
||||||
|
# root pts/0 10.211.55.2 Tue Nov 22 21:06 - 23:16 (02:10:00)
|
||||||
|
# root pts/1 10.211.55.2 Wed Jun 29 13:13 - shutdown (7+00:01:20)
|
||||||
return ($1, $2, $3, $4, $5 eq "shutdown" ? "Shutdown" :
|
return ($1, $2, $3, $4, $5 eq "shutdown" ? "Shutdown" :
|
||||||
$5 eq "crash" ? "Crash" : $5, $6);
|
$5 eq "crash" ? "Crash" : $5, $6);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ sub open_last_command
|
|||||||
{
|
{
|
||||||
local ($fh, $user) = @_;
|
local ($fh, $user) = @_;
|
||||||
local $quser = quotemeta($user);
|
local $quser = quotemeta($user);
|
||||||
open($fh, "(last -F $quser || last $quser) |");
|
open($fh, "(last -F -w $quser || last -w $quser) |");
|
||||||
}
|
}
|
||||||
|
|
||||||
# read_last_line(handle)
|
# read_last_line(handle)
|
||||||
@@ -39,12 +39,16 @@ while(1) {
|
|||||||
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
||||||
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\-\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\((\d+:\d+)\)/) {
|
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\-\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+\((.*?\d+:\d+)\)/) {
|
||||||
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
# jcameron pts/0 fudu Thu Feb 22 09:47 - 10:15
|
||||||
|
# jcameron pts/0 fudu Sun Feb 4 02:26:28 2024 - Wed Feb 7 18:25:09 2024 (3+15:58)
|
||||||
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
return ($1, $2, $3, $4, $5 eq "down" ? "Shutdown" : $5, $6);
|
||||||
}
|
}
|
||||||
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+still/) {
|
elsif ($line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+)\s+(still|gone)/ ||
|
||||||
# root pts/0 fudu Fri Feb 23 18:46 still logged in
|
$line =~ /^(\S+)\s+(\S+)\s+(\S+)?\s+(\S+\s+\S+\s+\d+\s+\d+:\d+:\d+\s+\d+)\s+(still|gone)/) {
|
||||||
|
# root pts/0 fudu Fri Feb 23 18:46 still logged in
|
||||||
|
# root ftpd10 fudu Thu Jun 20 11:19 gone - no logout
|
||||||
|
# root pts/0 fudu Tue Jun 18 23:10:30 2024 still logged in
|
||||||
return ($1, $2, $3, $4);
|
return ($1, $2, $3, $4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,18 @@ else {
|
|||||||
$miniserv{'bind'} = $first->[0];
|
$miniserv{'bind'} = $first->[0];
|
||||||
}
|
}
|
||||||
$miniserv{'sockets'} = join(" ", map { "$_->[0]:$_->[1]" } @sockets);
|
$miniserv{'sockets'} = join(" ", map { "$_->[0]:$_->[1]" } @sockets);
|
||||||
|
if ($in{'websocket_base_port_def'}) {
|
||||||
|
delete($miniserv{'websocket_base_port'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$miniserv{'websocket_base_port'} = $in{'websocket_base_port'};
|
||||||
|
}
|
||||||
|
if ($in{'websocket_host_def'}) {
|
||||||
|
delete($miniserv{'websocket_host'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$miniserv{'websocket_host'} = $in{'websocket_host'};
|
||||||
|
}
|
||||||
$miniserv{'ipv6'} = $in{'ipv6'};
|
$miniserv{'ipv6'} = $in{'ipv6'};
|
||||||
if ($in{'listen_def'}) {
|
if ($in{'listen_def'}) {
|
||||||
delete($miniserv{'listen'});
|
delete($miniserv{'listen'});
|
||||||
|
|||||||
@@ -47,6 +47,22 @@ if (&foreign_check("firewall")) {
|
|||||||
}
|
}
|
||||||
print &ui_table_row($text{'bind_sockets'}, $stable);
|
print &ui_table_row($text{'bind_sockets'}, $stable);
|
||||||
|
|
||||||
|
# WebSocket based port
|
||||||
|
print &ui_table_row($text{'bind_websocport'},
|
||||||
|
&ui_radio("websocket_base_port_def",
|
||||||
|
$miniserv{"websocket_base_port"} ? 0 : 1,
|
||||||
|
[ [ 1, $text{'bind_websocport_none'} ],
|
||||||
|
[ 0, &ui_textbox("websocket_base_port",
|
||||||
|
$miniserv{"websocket_base_port"}, 6) ] ]));
|
||||||
|
|
||||||
|
# Hostname for WebSocket connections
|
||||||
|
print &ui_table_row($text{'bind_websoc_host'},
|
||||||
|
&ui_radio("websocket_host_def",
|
||||||
|
$miniserv{"websocket_host"} ? 0 : 1,
|
||||||
|
[ [ 1, $text{'bind_websoc_host_auto'} ],
|
||||||
|
[ 0, &ui_textbox("websocket_host",
|
||||||
|
$miniserv{"websocket_host"}, 25) ] ]));
|
||||||
|
|
||||||
# IPv6 enabled?
|
# IPv6 enabled?
|
||||||
print &ui_table_row($text{'bind_ipv6'},
|
print &ui_table_row($text{'bind_ipv6'},
|
||||||
&ui_yesno_radio("ipv6", $miniserv{'ipv6'}));
|
&ui_yesno_radio("ipv6", $miniserv{'ipv6'}));
|
||||||
|
|||||||
332
web-lib-funcs.pl
332
web-lib-funcs.pl
@@ -2098,10 +2098,10 @@ formatted like dd/mmm/yyyy hh:mm:ss. Parameters are :
|
|||||||
|
|
||||||
=item seconds - Unix time is seconds to convert.
|
=item seconds - Unix time is seconds to convert.
|
||||||
|
|
||||||
=item date-only-or-opts - If set to 1, exclude the time from the returned string.
|
=item date-only-or-opts - If set to 1 exclude the time from the returned string.
|
||||||
|
|
||||||
In case this param is a hash reference use it for options in a new DateTime::Locale
|
In case this param is a hash reference use it for options in a new
|
||||||
code or preserve the original, old logic
|
DateTime::Locale code or preserve the original, old logic
|
||||||
|
|
||||||
=item fmt - Optional, one of dd/mon/yyyy, dd/mm/yyyy, mm/dd/yyyy or yyyy/mm/dd
|
=item fmt - Optional, one of dd/mon/yyyy, dd/mm/yyyy, mm/dd/yyyy or yyyy/mm/dd
|
||||||
|
|
||||||
@@ -2116,27 +2116,31 @@ if (!$@ && $] > 5.011) {
|
|||||||
my $opts = ref($only) ? $only : {};
|
my $opts = ref($only) ? $only : {};
|
||||||
my $locale_default = &get_default_system_locale();
|
my $locale_default = &get_default_system_locale();
|
||||||
my $locale_auto = &parse_accepted_language();
|
my $locale_auto = &parse_accepted_language();
|
||||||
my $locale_name = $opts->{'locale'} || $gconfig{'locale_'.$base_remote_user} ||
|
my $locale_name = $opts->{'locale'} ||
|
||||||
|
$gconfig{'locale_'.$base_remote_user} ||
|
||||||
$gconfig{'locale_'.$remote_user} || $locale_auto ||
|
$gconfig{'locale_'.$remote_user} || $locale_auto ||
|
||||||
$gconfig{'locale'} || &get_default_system_locale();
|
$gconfig{'locale'} || &get_default_system_locale();
|
||||||
my $tz = $opts->{'tz'};
|
my $tz = $opts->{'tz'};
|
||||||
if (!$tz) {
|
if (!$tz) {
|
||||||
eval {
|
eval {
|
||||||
$tz =
|
$tz = DateTime::TimeZone->new(
|
||||||
DateTime::TimeZone->new(name => strftime("%z", localtime()))->name(); # +0200
|
name => strftime("%z", localtime()))->name(); # +0200
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
eval {
|
eval {
|
||||||
$tz = DateTime::TimeZone->new(name => 'local')->name(); # Asia/Nicosia
|
$tz = DateTime::TimeZone->new(
|
||||||
|
name => 'local')->name(); # Asia/Nicosia
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$tz = DateTime::TimeZone->new(name => 'UTC')->name(); # UTC
|
$tz = DateTime::TimeZone->new(
|
||||||
|
name => 'UTC')->name(); # UTC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Pre-process time locale
|
# Pre-process time locale
|
||||||
my $locale_military_status = sub {
|
my $locale_military_status = sub {
|
||||||
return ($locale_military_name && $locale_military_name =~ /[a-z]/i) ? 2 :
|
return ($locale_military_name &&
|
||||||
|
$locale_military_name =~ /[a-z]/i) ? 2 :
|
||||||
($locale_military_name == 1) ? 1 : 0;
|
($locale_military_name == 1) ? 1 : 0;
|
||||||
};
|
};
|
||||||
# Allow locales with military time (in 24h format)
|
# Allow locales with military time (in 24h format)
|
||||||
@@ -2152,15 +2156,18 @@ if (!$@ && $] > 5.011) {
|
|||||||
my $locale = DateTime::Locale->load($locale_name_loaded);
|
my $locale = DateTime::Locale->load($locale_name_loaded);
|
||||||
# Create a new locale out of base locale
|
# Create a new locale out of base locale
|
||||||
if (&$locale_military_status() == 1) {
|
if (&$locale_military_status() == 1) {
|
||||||
my %locale_data = $locale->locale_data;
|
my %locale_data = $locale->locale_data;
|
||||||
$locale_data{'code'} = $locale_name_initial;
|
$locale_data{'code'} = $locale_name_initial;
|
||||||
|
|
||||||
# Force 24h time
|
# Force 24h time
|
||||||
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
|
$locale_data{'glibc_date_1_format'} = '%a %b %e %H:%M:%S %Z %Y';
|
||||||
$locale_data{'glibc_datetime_format'} = '%a %d %b %Y %T %Z';
|
$locale_data{'glibc_datetime_format'} = '%a %d %b %Y %T %Z';
|
||||||
$locale_data{'glibc_time_format'} = '%T';
|
$locale_data{'glibc_time_format'} = '%T';
|
||||||
DateTime::Locale->register_from_data(%locale_data);
|
DateTime::Locale->register_from_data(%locale_data);
|
||||||
|
|
||||||
# Load newly cloned locale in 24h time format
|
# Load newly cloned locale in 24h time format
|
||||||
$locale_military_name = $locale_name_loaded = $locale_name_initial;
|
$locale_military_name = $locale_name_loaded =
|
||||||
|
$locale_name_initial;
|
||||||
$locale = DateTime::Locale->load($locale_name_loaded);
|
$locale = DateTime::Locale->load($locale_name_loaded);
|
||||||
}
|
}
|
||||||
my $locale_format_full_tz = $locale->glibc_date_1_format; # Sat 20 Nov 2286 17:46:39 UTC
|
my $locale_format_full_tz = $locale->glibc_date_1_format; # Sat 20 Nov 2286 17:46:39 UTC
|
||||||
@@ -2176,7 +2183,7 @@ if (!$@ && $] > 5.011) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Return fully detailed object
|
# Return fully detailed object
|
||||||
if (%{$opts}) {
|
if (ref($only)) {
|
||||||
# Can we get ago time
|
# Can we get ago time
|
||||||
my $ago;
|
my $ago;
|
||||||
my $ago_secs = time() - $secs;
|
my $ago_secs = time() - $secs;
|
||||||
@@ -2194,18 +2201,31 @@ if (!$@ && $] > 5.011) {
|
|||||||
"pretty" => $ago_obj->pretty
|
"pretty" => $ago_obj->pretty
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
# my $xxxx = $locale->full_date_format;
|
|
||||||
my $data = {
|
my $data = {
|
||||||
# Wed Feb 8 05:09:39 PM UTC 2023
|
# Wed Feb 8 05:09:39 PM UTC 2023
|
||||||
'full-tz-utc' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs)->strftime($locale_format_full_tz),
|
'full-tz-utc' => DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded,
|
||||||
|
epoch => $secs)->strftime($locale_format_full_tz),
|
||||||
# Wed Feb 8 07:10:01 PM EET 2023
|
# Wed Feb 8 07:10:01 PM EET 2023
|
||||||
'full-tz' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full_tz),
|
'full-tz' => DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded,
|
||||||
|
epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime($locale_format_full_tz),
|
||||||
# Wed 08 Feb 2023 07:11:26 PM EET
|
# Wed 08 Feb 2023 07:11:26 PM EET
|
||||||
'full' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_full),
|
'full' => DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded,
|
||||||
|
epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime($locale_format_full),
|
||||||
# 02/08/2023
|
# 02/08/2023
|
||||||
'short' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_short),
|
'short' => DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded,
|
||||||
|
epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime($locale_format_short),
|
||||||
# 07:12:07 PM
|
# 07:12:07 PM
|
||||||
'time' => DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime($locale_format_time),
|
'time' => DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded,
|
||||||
|
epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime($locale_format_time),
|
||||||
'ago' => $ago,
|
'ago' => $ago,
|
||||||
'tz' => $tz,
|
'tz' => $tz,
|
||||||
'delimiter' => $locale_format_delimiter,
|
'delimiter' => $locale_format_delimiter,
|
||||||
@@ -2216,10 +2236,16 @@ if (!$@ && $] > 5.011) {
|
|||||||
$data->{'timeshort'} = $data->{'time'};
|
$data->{'timeshort'} = $data->{'time'};
|
||||||
$data->{'timeshort'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
$data->{'timeshort'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
||||||
|
|
||||||
# %c alternative with full week and month and no seconds in time (complete)
|
# %c alternative with full week and month and no seconds
|
||||||
# Wednesday, February 8, 2023, 8:18 PM or 星期三, 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
|
# in time (complete)
|
||||||
$data->{'monthfull'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%B");
|
# Wednesday, February 8, 2023, 8:18 PM or 星期三,
|
||||||
foreach (split(/\s+/, DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%A, %c"))) {
|
# 2023年2月8日 20:18 or miércoles, 8 febrero 2023, 20:28
|
||||||
|
$data->{'monthfull'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%B");
|
||||||
|
foreach (split(/\s+/, DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%A, %c"))) {
|
||||||
if ($data->{'monthfull'} =~ /^$_/) {
|
if ($data->{'monthfull'} =~ /^$_/) {
|
||||||
$data->{'complete'} .= "$data->{'monthfull'} "
|
$data->{'complete'} .= "$data->{'monthfull'} "
|
||||||
}
|
}
|
||||||
@@ -2227,9 +2253,24 @@ if (!$@ && $] > 5.011) {
|
|||||||
$data->{'complete'} .= "$_ "
|
$data->{'complete'} .= "$_ "
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$data->{'year'} = DateTime->from_epoch(locale => $locale_name_loaded, epoch => $secs, time_zone => $tz)->strftime("%Y");
|
$data->{'year'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%Y");
|
||||||
|
$data->{'day'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%d");
|
||||||
|
$data->{'week'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%a");
|
||||||
|
$data->{'weekfull'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%A");
|
||||||
|
$data->{'month'} = DateTime->from_epoch(
|
||||||
|
locale => $locale_name_loaded, epoch => $secs,
|
||||||
|
time_zone => $tz)->strftime("%b");
|
||||||
$data->{'complete'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
$data->{'complete'} =~ s/(\d+):(\d+):(\d+)(.*?)/$1:$2$4/;
|
||||||
($data->{'complete_short'} = $data->{'complete'}) =~ s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
|
($data->{'complete_short'} = $data->{'complete'}) =~
|
||||||
|
s/(.*?)([\s\,]*\Q$data->{'year'}\E.*)/$1/;
|
||||||
|
|
||||||
if ($opts->{'get'}) {
|
if ($opts->{'get'}) {
|
||||||
return $data->{$opts->{'get'}};
|
return $data->{$opts->{'get'}};
|
||||||
@@ -7969,6 +8010,100 @@ return ref($s) && $s->{'host'} && $s->{'port'} ?
|
|||||||
ref($s) ? "" : "$s.$$";
|
ref($s) ? "" : "$s.$$";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 verify_session_id(session-id, [sessiondb], [miniserv])
|
||||||
|
|
||||||
|
Returns the username (or an array with user, last login, and IP address) for the
|
||||||
|
given session (or session hash) ID, or undefined if session ID is invalid.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
|
||||||
|
$sid: The session ID to verify
|
||||||
|
|
||||||
|
$sessiondb: A reference to the session database hash (optional)
|
||||||
|
|
||||||
|
$miniserv: A reference to the miniserv configuration hash (optional)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
In list context: A list containing the user, last activity time, and IP
|
||||||
|
address associated with the session ID.
|
||||||
|
|
||||||
|
In scalar context: The user associated with the session ID.
|
||||||
|
|
||||||
|
If the session ID is not found, returns an empty list in list context
|
||||||
|
or undef in scalar context.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
Retrieve the username associated with the session ID or undef if
|
||||||
|
session is invalid.
|
||||||
|
|
||||||
|
my $user = verify_session_id($main::session_id);
|
||||||
|
Example of return:
|
||||||
|
root
|
||||||
|
|
||||||
|
Retrieve array containing the user, last login, and IP address
|
||||||
|
for given session hash ID or undef if session is invalid.
|
||||||
|
|
||||||
|
my (@user) = verify_session_id('BSRxr6wpF25lqeRinQ/sv0');
|
||||||
|
Example of return:
|
||||||
|
[
|
||||||
|
'root',
|
||||||
|
'1719401071',
|
||||||
|
'10.211.55.2'
|
||||||
|
]
|
||||||
|
|
||||||
|
Retrieve the username associated with the session ID, using given
|
||||||
|
session DB or undef if session is invalid.
|
||||||
|
|
||||||
|
my %sessiondb;
|
||||||
|
dbmopen(%sessiondb, "$var_directory/sessiondb", 0400);
|
||||||
|
my $user = verify_session_id($main::session_id, \%sessiondb);
|
||||||
|
dbmclose(%sessiondb);
|
||||||
|
Example of return:
|
||||||
|
someuser1
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub verify_session_id
|
||||||
|
{
|
||||||
|
my ($sid, $sessiondb, $miniserv) = @_;
|
||||||
|
my $hashsessionidfunc = \&miniserv::hash_session_id;
|
||||||
|
my %miniserv;
|
||||||
|
if ($miniserv) {
|
||||||
|
# Use provided miniserv configuration
|
||||||
|
%miniserv = %{$miniserv};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Load miniserv configuration if not provided
|
||||||
|
&get_miniserv_config(\%miniserv);
|
||||||
|
}
|
||||||
|
my %sessiondb_;
|
||||||
|
if ($sessiondb) {
|
||||||
|
# Use provided session database
|
||||||
|
%sessiondb_ = %{$sessiondb};
|
||||||
|
}
|
||||||
|
elsif (&foreign_available('acl')) {
|
||||||
|
# Use session database using ACL module API
|
||||||
|
&foreign_require("acl");
|
||||||
|
&acl::open_session_db(\%miniserv);
|
||||||
|
$hashsessionidfunc = \&acl::hash_session_id;
|
||||||
|
%sessiondb_ = %acl::sessiondb;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return wantarray ? ( ) : undef;
|
||||||
|
}
|
||||||
|
# Verify given session (hash) ID against the session database
|
||||||
|
foreach my $k (grep { $sessiondb_{$_} } keys %sessiondb_) {
|
||||||
|
if ($k eq $sid ||
|
||||||
|
(defined($hashsessionidfunc) && $k eq $hashsessionidfunc->($sid))) {
|
||||||
|
my ($user, $last, $ip) = split(/\s+/, $sessiondb_{$k});
|
||||||
|
return wantarray ? ($user, $last, $ip) : $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Return an empty list or undef if session ID is not found
|
||||||
|
return wantarray ? ( ) : undef;
|
||||||
|
}
|
||||||
|
|
||||||
=head2 remote_foreign_require(server, module, file)
|
=head2 remote_foreign_require(server, module, file)
|
||||||
|
|
||||||
Connects to rpc.cgi on a remote webmin server and have it open a session
|
Connects to rpc.cgi on a remote webmin server and have it open a session
|
||||||
@@ -13522,6 +13657,149 @@ my $dir = $var_directory."/locks/".$$;
|
|||||||
return $dir;
|
return $dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# allocate_miniserv_websocket([module])
|
||||||
|
# Allocate a new websocket and
|
||||||
|
# stores it miniserv.conf file
|
||||||
|
sub allocate_miniserv_websocket
|
||||||
|
{
|
||||||
|
my ($module) = @_;
|
||||||
|
$module ||= $module_name;
|
||||||
|
# Find ports already in use
|
||||||
|
&lock_file(&get_miniserv_config_file());
|
||||||
|
my %miniserv;
|
||||||
|
&get_miniserv_config(\%miniserv);
|
||||||
|
my %inuse;
|
||||||
|
foreach my $k (keys %miniserv) {
|
||||||
|
if ($k =~ /^websockets_/ && $miniserv{$k} =~ /port=(\d+)/) {
|
||||||
|
$inuse{$1} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pick a port and configure Webmin to proxy it
|
||||||
|
my $port = $miniserv{'websocket_base_port'} || 555;
|
||||||
|
while(1) {
|
||||||
|
if (!$inuse{$port}) {
|
||||||
|
&open_socket("127.0.0.1", $port, my $fh, \$err);
|
||||||
|
last if ($err);
|
||||||
|
close($fh);
|
||||||
|
}
|
||||||
|
$port++;
|
||||||
|
}
|
||||||
|
my $wspath = "/$module/ws-".$port;
|
||||||
|
my $now = time();
|
||||||
|
$miniserv{'websockets_'.$wspath} = "host=127.0.0.1 port=$port wspath=/ user=$remote_user time=$now";
|
||||||
|
&put_miniserv_config(\%miniserv);
|
||||||
|
&unlock_file(&get_miniserv_config_file());
|
||||||
|
&reload_miniserv();
|
||||||
|
return $port;
|
||||||
|
}
|
||||||
|
|
||||||
|
# get_miniserv_websocket_url(port, [host], [module])
|
||||||
|
# Returns the URL for a websocket
|
||||||
|
sub get_miniserv_websocket_url
|
||||||
|
{
|
||||||
|
my ($port, $host, $module) = @_;
|
||||||
|
$module ||= $module_name;
|
||||||
|
my $ws_proto = lc($ENV{'HTTPS'}) eq 'on' ? 'wss' : 'ws';
|
||||||
|
my %miniserv;
|
||||||
|
&get_miniserv_config(\%miniserv);
|
||||||
|
my $http_host_conf = &trim($miniserv{'websocket_host'} || $host);
|
||||||
|
# Pass as defined
|
||||||
|
if ($http_host_conf) {
|
||||||
|
if ($http_host_conf !~ /^wss?:\/\//) {
|
||||||
|
$http_host_conf = "$ws_proto://$http_host_conf";
|
||||||
|
}
|
||||||
|
$http_host_conf =~ s/[\/]+$//g;
|
||||||
|
}
|
||||||
|
# Try to rely on the proxy
|
||||||
|
if (!defined($http_host_conf)) {
|
||||||
|
my $forwarded_host = $ENV{'HTTP_X_FORWARDED_HOST'};
|
||||||
|
if ($forwarded_host) {
|
||||||
|
$http_host_conf = "$ws_proto://$forwarded_host".
|
||||||
|
&get_webprefix();
|
||||||
|
$http_host_conf =~ s/\/$//;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my $http_host = $http_host_conf || "$ws_proto://$ENV{'HTTP_HOST'}";
|
||||||
|
return "$http_host/$module/ws-$port";
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove_miniserv_websocket(port, [module])
|
||||||
|
# Remove old websocket from miniserv.conf
|
||||||
|
sub remove_miniserv_websocket
|
||||||
|
{
|
||||||
|
my ($port, $module) = @_;
|
||||||
|
$module ||= $module_name;
|
||||||
|
my %miniserv;
|
||||||
|
if ($port) {
|
||||||
|
&lock_file(&get_miniserv_config_file());
|
||||||
|
&get_miniserv_config(\%miniserv);
|
||||||
|
my $wspath = "/$module/ws-".$port;
|
||||||
|
if ($miniserv{'websockets_'.$wspath}) {
|
||||||
|
delete($miniserv{'websockets_'.$wspath});
|
||||||
|
&put_miniserv_config(\%miniserv);
|
||||||
|
&reload_miniserv();
|
||||||
|
}
|
||||||
|
&unlock_file(&get_miniserv_config_file());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# cleanup_miniserv_websockets([&skip-ports], [module])
|
||||||
|
# Called by scheduled status collection to remove any
|
||||||
|
# websockets in miniserv.conf that are no longer used
|
||||||
|
sub cleanup_miniserv_websockets
|
||||||
|
{
|
||||||
|
my ($skip, $module) = @_;
|
||||||
|
$skip ||= [ ];
|
||||||
|
$module ||= $module_name;
|
||||||
|
&lock_file(&get_miniserv_config_file());
|
||||||
|
my %miniserv;
|
||||||
|
&get_miniserv_config(\%miniserv);
|
||||||
|
my $now = time();
|
||||||
|
my @clean;
|
||||||
|
foreach my $k (keys %miniserv) {
|
||||||
|
$k =~ /^websockets_\/$module\/ws-(\d+)$/ || next;
|
||||||
|
my $port = $1;
|
||||||
|
next if (&indexof($port, @$skip) >= 0);
|
||||||
|
my $when = 0;
|
||||||
|
if ($miniserv{$k} =~ /time=(\d+)/) {
|
||||||
|
$when = $1;
|
||||||
|
}
|
||||||
|
if ($now - $when > 60) {
|
||||||
|
# Has been open for a while, check if the port is still in use?
|
||||||
|
my $err;
|
||||||
|
&open_socket("127.0.0.1", $port, my $fh, \$err);
|
||||||
|
if ($err) {
|
||||||
|
# Closed now, can clean up
|
||||||
|
push(@clean, $k);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# Still active
|
||||||
|
close($fh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (@clean) {
|
||||||
|
foreach my $k (@clean) {
|
||||||
|
delete($miniserv{$k});
|
||||||
|
}
|
||||||
|
&put_miniserv_config(\%miniserv);
|
||||||
|
&reload_miniserv();
|
||||||
|
}
|
||||||
|
&unlock_file(&get_miniserv_config_file());
|
||||||
|
}
|
||||||
|
|
||||||
|
# get_miniserv_websockets_modules()
|
||||||
|
# Returns a list of modules and themes that use websockets
|
||||||
|
sub get_miniserv_websockets_modules
|
||||||
|
{
|
||||||
|
my @rv;
|
||||||
|
foreach my $i (&get_all_module_infos(), &list_themes()) {
|
||||||
|
push(@rv, $i->{'dir'}) if ($i->{'websockets'});
|
||||||
|
}
|
||||||
|
return @rv;
|
||||||
|
}
|
||||||
|
|
||||||
$done_web_lib_funcs = 1;
|
$done_web_lib_funcs = 1;
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|||||||
@@ -84,6 +84,18 @@ else {
|
|||||||
$miniserv{'bind'} = $first->[0];
|
$miniserv{'bind'} = $first->[0];
|
||||||
}
|
}
|
||||||
$miniserv{'sockets'} = join(" ", map { "$_->[0]:$_->[1]" } @sockets);
|
$miniserv{'sockets'} = join(" ", map { "$_->[0]:$_->[1]" } @sockets);
|
||||||
|
if ($in{'websocket_base_port_def'}) {
|
||||||
|
delete($miniserv{'websocket_base_port'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$miniserv{'websocket_base_port'} = $in{'websocket_base_port'};
|
||||||
|
}
|
||||||
|
if ($in{'websocket_host_def'}) {
|
||||||
|
delete($miniserv{'websocket_host'});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$miniserv{'websocket_host'} = $in{'websocket_host'};
|
||||||
|
}
|
||||||
$miniserv{'ipv6'} = $in{'ipv6'};
|
$miniserv{'ipv6'} = $in{'ipv6'};
|
||||||
if ($in{'listen_def'}) {
|
if ($in{'listen_def'}) {
|
||||||
delete($miniserv{'listen'});
|
delete($miniserv{'listen'});
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ if (&foreign_check("firewall")) {
|
|||||||
}
|
}
|
||||||
print &ui_table_row($text{'bind_sockets'}, $stable);
|
print &ui_table_row($text{'bind_sockets'}, $stable);
|
||||||
|
|
||||||
|
# WebSocket based port
|
||||||
|
print &ui_table_row($text{'bind_websocport'},
|
||||||
|
&ui_radio("websocket_base_port_def",
|
||||||
|
$miniserv{"websocket_base_port"} ? 0 : 1,
|
||||||
|
[ [ 1, $text{'bind_websocport_none'} ],
|
||||||
|
[ 0, &ui_textbox("websocket_base_port",
|
||||||
|
$miniserv{"websocket_base_port"}, 6) ] ]));
|
||||||
|
|
||||||
|
# Hostname for WebSocket connections
|
||||||
|
print &ui_table_row($text{'bind_websoc_host'},
|
||||||
|
&ui_radio("websocket_host_def",
|
||||||
|
$miniserv{"websocket_host"} ? 0 : 1,
|
||||||
|
[ [ 1, $text{'bind_websoc_host_auto'} ],
|
||||||
|
[ 0, &ui_textbox("websocket_host",
|
||||||
|
$miniserv{"websocket_host"}, 25) ] ]));
|
||||||
|
|
||||||
# IPv6 enabled?
|
# IPv6 enabled?
|
||||||
print &ui_table_row($text{'bind_ipv6'},
|
print &ui_table_row($text{'bind_ipv6'},
|
||||||
&ui_yesno_radio("ipv6", $miniserv{'ipv6'}));
|
&ui_yesno_radio("ipv6", $miniserv{'ipv6'}));
|
||||||
|
|||||||
@@ -277,7 +277,9 @@ else {
|
|||||||
my @doms = $config{'letsencrypt_doms'} ?
|
my @doms = $config{'letsencrypt_doms'} ?
|
||||||
split(/\s+/, $config{'letsencrypt_doms'}) : ( $host );
|
split(/\s+/, $config{'letsencrypt_doms'}) : ( $host );
|
||||||
print &ui_table_row($text{'ssl_letsdoms'},
|
print &ui_table_row($text{'ssl_letsdoms'},
|
||||||
&ui_textarea("dom", join("\n", @doms), 5, 40));
|
&ui_textarea("dom", join("\n", @doms), 5, 40)."<br>\n".
|
||||||
|
&ui_checkbox("subset", 1, $text{'ssl_subset'},
|
||||||
|
$config{'letsencrypt_subset'}));
|
||||||
|
|
||||||
# Apache vhost or other path
|
# Apache vhost or other path
|
||||||
my @opts;
|
my @opts;
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ bind_sport0=Same as first
|
|||||||
bind_sport1=Specific port ..
|
bind_sport1=Specific port ..
|
||||||
bind_listen=Listen for broadcasts on UDP port
|
bind_listen=Listen for broadcasts on UDP port
|
||||||
bind_none=Don't listen
|
bind_none=Don't listen
|
||||||
|
bind_websocport=Base port number for WebSockets connections
|
||||||
|
bind_websocport_none=Default (555)
|
||||||
|
bind_websoc_host=Hostname for WebSocket connections
|
||||||
|
bind_websoc_host_auto=Automatic
|
||||||
bind_hostname=Web server hostname
|
bind_hostname=Web server hostname
|
||||||
bind_auto=Work out from browser
|
bind_auto=Work out from browser
|
||||||
bind_err=Failed to change address
|
bind_err=Failed to change address
|
||||||
@@ -420,6 +424,7 @@ ssl_letserr2=Alternately, check the <a href='$1'>module configuration</a> page t
|
|||||||
ssl_letsdesc2=This page can be used to request a new certificate, which will overwrite any other currently have configured in Webmin. However, the Let's Encrypt service requires that your ownership of the certificate domain be validated by checking that this system hosts the website for the domain. This is done by placing a small temporary file in the website's document directory.
|
ssl_letsdesc2=This page can be used to request a new certificate, which will overwrite any other currently have configured in Webmin. However, the Let's Encrypt service requires that your ownership of the certificate domain be validated by checking that this system hosts the website for the domain. This is done by placing a small temporary file in the website's document directory.
|
||||||
ssl_letsheader=Options for new SSL certificate
|
ssl_letsheader=Options for new SSL certificate
|
||||||
ssl_letsdoms=Hostnames for certificate
|
ssl_letsdoms=Hostnames for certificate
|
||||||
|
ssl_subset=Skip unverifiable hostnames?
|
||||||
ssl_letsmode=Let's Encrypt validation method
|
ssl_letsmode=Let's Encrypt validation method
|
||||||
ssl_letsmode0=Apache virtual host matching hostname
|
ssl_letsmode0=Apache virtual host matching hostname
|
||||||
ssl_letsmode1=Selected Apache virtual host
|
ssl_letsmode1=Selected Apache virtual host
|
||||||
|
|||||||
@@ -58,14 +58,15 @@ return &software::missing_install_link(
|
|||||||
|
|
||||||
# request_letsencrypt_cert(domain|&domains, webroot, [email], [keysize],
|
# request_letsencrypt_cert(domain|&domains, webroot, [email], [keysize],
|
||||||
# [request-mode], [use-staging], [account-email],
|
# [request-mode], [use-staging], [account-email],
|
||||||
# [reuse-key], [server-url, server-key, server-hmac])
|
# [reuse-key], [server-url, server-key, server-hmac],
|
||||||
|
# [allow-subset])
|
||||||
# Attempt to request a cert using a generated key with the Let's Encrypt client
|
# Attempt to request a cert using a generated key with the Let's Encrypt client
|
||||||
# command, and write it to the given path. Returns a status flag, and either
|
# command, and write it to the given path. Returns a status flag, and either
|
||||||
# an error message or the paths to cert, key and chain files.
|
# an error message or the paths to cert, key and chain files.
|
||||||
sub request_letsencrypt_cert
|
sub request_letsencrypt_cert
|
||||||
{
|
{
|
||||||
my ($dom, $webroot, $email, $size, $mode, $staging, $account_email,
|
my ($dom, $webroot, $email, $size, $mode, $staging, $account_email,
|
||||||
$key_type, $reuse_key, $server, $server_key, $server_hmac) = @_;
|
$key_type, $reuse_key, $server, $server_key, $server_hmac, $subset) = @_;
|
||||||
my @doms = ref($dom) ? @$dom : ($dom);
|
my @doms = ref($dom) ? @$dom : ($dom);
|
||||||
$email ||= "root\@$doms[0]";
|
$email ||= "root\@$doms[0]";
|
||||||
$mode ||= "web";
|
$mode ||= "web";
|
||||||
@@ -179,6 +180,7 @@ if ($letsencrypt_cmd) {
|
|||||||
my $new_flags = "";
|
my $new_flags = "";
|
||||||
my $reuse_flags = "";
|
my $reuse_flags = "";
|
||||||
my $server_flags = "";
|
my $server_flags = "";
|
||||||
|
my $subset_flags = "";
|
||||||
$key_type ||= $config{'letsencrypt_algo'} || 'rsa';
|
$key_type ||= $config{'letsencrypt_algo'} || 'rsa';
|
||||||
if (&compare_version_numbers($cmd_ver, 1.11) < 0) {
|
if (&compare_version_numbers($cmd_ver, 1.11) < 0) {
|
||||||
$old_flags = " --manual-public-ip-logging-ok";
|
$old_flags = " --manual-public-ip-logging-ok";
|
||||||
@@ -192,6 +194,9 @@ if ($letsencrypt_cmd) {
|
|||||||
else {
|
else {
|
||||||
$reuse_flags = " --no-reuse-key";
|
$reuse_flags = " --no-reuse-key";
|
||||||
}
|
}
|
||||||
|
if ($subset) {
|
||||||
|
$subset_flags = " --allow-subset-of-names";
|
||||||
|
}
|
||||||
$reuse_flags = "" if ($reuse_key && $reuse_key == -1);
|
$reuse_flags = "" if ($reuse_key && $reuse_key == -1);
|
||||||
if ($server) {
|
if ($server) {
|
||||||
$server_flags = " --server ".quotemeta($server);
|
$server_flags = " --server ".quotemeta($server);
|
||||||
@@ -227,6 +232,7 @@ if ($letsencrypt_cmd) {
|
|||||||
$old_flags.
|
$old_flags.
|
||||||
$server_flags.
|
$server_flags.
|
||||||
$new_flags.
|
$new_flags.
|
||||||
|
$subset_flags.
|
||||||
" 2>&1)");
|
" 2>&1)");
|
||||||
&reset_environment();
|
&reset_environment();
|
||||||
}
|
}
|
||||||
@@ -245,6 +251,7 @@ if ($letsencrypt_cmd) {
|
|||||||
$old_flags.
|
$old_flags.
|
||||||
$server_flags.
|
$server_flags.
|
||||||
$new_flags.
|
$new_flags.
|
||||||
|
$subset_flags.
|
||||||
" 2>&1)");
|
" 2>&1)");
|
||||||
&reset_environment();
|
&reset_environment();
|
||||||
}
|
}
|
||||||
@@ -260,6 +267,7 @@ if ($letsencrypt_cmd) {
|
|||||||
$old_flags.
|
$old_flags.
|
||||||
$server_flags.
|
$server_flags.
|
||||||
$new_flags.
|
$new_flags.
|
||||||
|
$subset_flags.
|
||||||
" 2>&1)");
|
" 2>&1)");
|
||||||
&reset_environment();
|
&reset_environment();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ else {
|
|||||||
|
|
||||||
if ($in{'save'}) {
|
if ($in{'save'}) {
|
||||||
# Just update renewal
|
# Just update renewal
|
||||||
&save_renewal_only(\@doms, $webroot, $mode);
|
&save_renewal_only(\@doms, $webroot, $mode, $size, $in{'subset'});
|
||||||
&redirect("edit_ssl.cgi");
|
&redirect("edit_ssl.cgi");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -88,7 +88,9 @@ else {
|
|||||||
'letsencrypt_doing',
|
'letsencrypt_doing',
|
||||||
"<tt>".&html_escape(join(", ", @doms))."</tt>",
|
"<tt>".&html_escape(join(", ", @doms))."</tt>",
|
||||||
"<tt>".&html_escape($webroot)."</tt>"),"<p>\n";
|
"<tt>".&html_escape($webroot)."</tt>"),"<p>\n";
|
||||||
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(\@doms, $webroot, undef, $size, $mode, $in{'staging'});
|
my ($ok, $cert, $key, $chain) = &request_letsencrypt_cert(
|
||||||
|
\@doms, $webroot, undef, $size, $mode, $in{'staging'},
|
||||||
|
undef, 0, undef, undef, undef, $in{'subset'});
|
||||||
if (!$ok) {
|
if (!$ok) {
|
||||||
print &text('letsencrypt_failed', $cert),"<p>\n";
|
print &text('letsencrypt_failed', $cert),"<p>\n";
|
||||||
}
|
}
|
||||||
@@ -148,15 +150,16 @@ else {
|
|||||||
&ui_print_footer("", $text{'index_return'});
|
&ui_print_footer("", $text{'index_return'});
|
||||||
}
|
}
|
||||||
|
|
||||||
# save_renewal_only(&doms, webroot, mode)
|
# save_renewal_only(&doms, webroot, mode, size, subset-mode)
|
||||||
# Save for future renewals
|
# Save for future renewals
|
||||||
sub save_renewal_only
|
sub save_renewal_only
|
||||||
{
|
{
|
||||||
my ($doms, $webroot, $mode) = @_;
|
my ($doms, $webroot, $mode, $size, $subset) = @_;
|
||||||
$config{'letsencrypt_doms'} = join(" ", @$doms);
|
$config{'letsencrypt_doms'} = join(" ", @$doms);
|
||||||
$config{'letsencrypt_webroot'} = $webroot;
|
$config{'letsencrypt_webroot'} = $webroot;
|
||||||
$config{'letsencrypt_mode'} = $mode;
|
$config{'letsencrypt_mode'} = $mode;
|
||||||
$config{'letsencrypt_size'} = $size;
|
$config{'letsencrypt_size'} = $size;
|
||||||
|
$config{'letsencrypt_subset'} = $subset;
|
||||||
&save_module_config();
|
&save_module_config();
|
||||||
if (&foreign_check("webmincron")) {
|
if (&foreign_check("webmincron")) {
|
||||||
my $job = &find_letsencrypt_cron_job();
|
my $job = &find_letsencrypt_cron_job();
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ if (!$eol_data->{'_eol_timestamp'}) {
|
|||||||
&error_stderr("The provided data is not a valid EOL data hash reference");
|
&error_stderr("The provided data is not a valid EOL data hash reference");
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
my $eol_date = &make_date($eol_data->{'_eol_timestamp'}, { '_' => 1 });
|
my $eol_date = &make_date($eol_data->{'_eol_timestamp'}, { });
|
||||||
if (ref($eol_date)) {
|
if (ref($eol_date)) {
|
||||||
my $eol_in = sub {
|
my $eol_in = sub {
|
||||||
my $eol_date = shift;
|
my $eol_date = shift;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Functions for creating and listing Webmin scheduled functions.
|
|||||||
|
|
||||||
BEGIN { push(@INC, ".."); };
|
BEGIN { push(@INC, ".."); };
|
||||||
use WebminCore;
|
use WebminCore;
|
||||||
|
use feature 'state';
|
||||||
&init_config();
|
&init_config();
|
||||||
|
|
||||||
$webmin_crons_directory = "$module_config_directory/crons";
|
$webmin_crons_directory = "$module_config_directory/crons";
|
||||||
@@ -72,11 +73,12 @@ Create or update a webmin cron function. Also locks the file being written to.
|
|||||||
sub save_webmin_cron
|
sub save_webmin_cron
|
||||||
{
|
{
|
||||||
my ($cron) = @_;
|
my ($cron) = @_;
|
||||||
|
state $cnt = 0;
|
||||||
if (!-d $webmin_crons_directory) {
|
if (!-d $webmin_crons_directory) {
|
||||||
&make_dir($webmin_crons_directory, 0700);
|
&make_dir($webmin_crons_directory, 0700);
|
||||||
}
|
}
|
||||||
if (!$cron->{'id'}) {
|
if (!$cron->{'id'}) {
|
||||||
$cron->{'id'} = time().$$;
|
$cron->{'id'} = time().$$.($cnt++);
|
||||||
}
|
}
|
||||||
my $file = "$webmin_crons_directory/$cron->{'id'}.cron";
|
my $file = "$webmin_crons_directory/$cron->{'id'}.cron";
|
||||||
my %wcron = %$cron;
|
my %wcron = %$cron;
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ my ($o) = @_;
|
|||||||
print &ui_table_row($text{'acl_user'},
|
print &ui_table_row($text{'acl_user'},
|
||||||
&ui_opt_textbox("user", $o->{'user'} eq '*' ? undef : $o->{'user'},
|
&ui_opt_textbox("user", $o->{'user'} eq '*' ? undef : $o->{'user'},
|
||||||
20, $text{'acl_sameuser'}));
|
20, $text{'acl_sameuser'}));
|
||||||
|
|
||||||
|
print &ui_table_row($text{'acl_sudoenforce'},
|
||||||
|
&ui_yesno_radio("sudoenforce",
|
||||||
|
$o->{'sudoenforce'} == 1 ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
sub acl_security_save
|
sub acl_security_save
|
||||||
@@ -17,4 +21,5 @@ sub acl_security_save
|
|||||||
my ($o) = @_;
|
my ($o) = @_;
|
||||||
|
|
||||||
$o->{'user'} = $in{'user_def'} ? '*' : $in{'user'};
|
$o->{'user'} = $in{'user_def'} ? '*' : $in{'user'};
|
||||||
|
$o->{'sudoenforce'} = $in{'sudoenforce'} ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
xterm=Set <tt>TERM</tt> environmental variable to,4,xterm+256color-xterm-256color,xterm+16color-xterm-16color,xterm-xterm,vt102-vt102,vt100-vt100,vt52-vt52,rxvt-rxvt,nsterm-nsterm,dtterm-dtterm,ansi-ansi
|
xterm=Set <tt>TERM</tt> environmental variable to,4,xterm+256color-xterm-256color,xterm+16color-xterm-16color,xterm-xterm,vt102-vt102,vt100-vt100,vt52-vt52,rxvt-rxvt,nsterm-nsterm,dtterm-dtterm,ansi-ansi
|
||||||
base_port=Base port number for WebSockets connections,0,5
|
|
||||||
host=Hostname for WebSocket connections,3,Automatic,32,,,Manual
|
|
||||||
size=Terminal width and height in characters,3,Automatic,5,,,Static (80x24)
|
size=Terminal width and height in characters,3,Automatic,5,,,Static (80x24)
|
||||||
locale=Set shell character encoding,10,0-Shell default,1-<tt>en_US.UTF-8</tt>,Custom
|
locale=Set shell character encoding,10,0-Shell default,1-<tt>en_US.UTF-8</tt>,Custom
|
||||||
rcfile=Execute initialization commands from file,10,0-Shell default,1-Module default,Custom
|
rcfile=Execute initialization commands from file,10,0-Shell default,1-Module default,Custom
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
user=root
|
user=root
|
||||||
|
sudoenforce=1
|
||||||
|
|||||||
@@ -168,13 +168,21 @@ EOF
|
|||||||
print "<div data-label=\"$text{'index_connecting'}\" id=\"terminal\"></div>\n";
|
print "<div data-label=\"$text{'index_connecting'}\" id=\"terminal\"></div>\n";
|
||||||
|
|
||||||
# Get a free port that can be used for the socket
|
# Get a free port that can be used for the socket
|
||||||
my $port = &allocate_miniserv_websocket();
|
my $port = &allocate_miniserv_websocket($module_name);
|
||||||
|
|
||||||
# Check permissions for user to run as
|
# Check permissions for user to run as
|
||||||
my $user = $access{'user'};
|
my $user = $access{'user'};
|
||||||
if ($user eq "*") {
|
if ($user eq "*") {
|
||||||
$user = $remote_user;
|
$user = $remote_user;
|
||||||
}
|
}
|
||||||
|
elsif ($user eq "root" && $remote_user ne $user && !$in{'user'} &&
|
||||||
|
$access{'sudoenforce'} ne '0') {
|
||||||
|
# If possible, start with a sudo-capable user
|
||||||
|
my @uinfo = getpwnam($remote_user);
|
||||||
|
if (@uinfo && $uinfo[7]) {
|
||||||
|
$user = $remote_user;
|
||||||
|
}
|
||||||
|
}
|
||||||
$user = $config{'user'} if ($user eq 'root' && $config{'user'});
|
$user = $config{'user'} if ($user eq 'root' && $config{'user'});
|
||||||
|
|
||||||
# Switch to given user
|
# Switch to given user
|
||||||
@@ -194,23 +202,13 @@ my $shellserver_cmd = "$module_config_directory/shellserver.pl";
|
|||||||
if (!-r $shellserver_cmd) {
|
if (!-r $shellserver_cmd) {
|
||||||
&create_wrapper($shellserver_cmd, $module_name, "shellserver.pl");
|
&create_wrapper($shellserver_cmd, $module_name, "shellserver.pl");
|
||||||
}
|
}
|
||||||
my $tmpdir = &tempname_dir();
|
|
||||||
$ENV{'SESSION_ID'} = $main::session_id;
|
$ENV{'SESSION_ID'} = $main::session_id;
|
||||||
&system_logged($shellserver_cmd." ".quotemeta($port)." ".quotemeta($user).
|
&system_logged($shellserver_cmd." ".quotemeta($port)." ".quotemeta($user).
|
||||||
($dir ? " ".quotemeta($dir) : "").
|
($dir ? " ".quotemeta($dir) : "").
|
||||||
" >$tmpdir/ws-$port.out 2>&1 </dev/null");
|
" >$module_var_directory/websocket-connection-$port.out 2>&1 </dev/null");
|
||||||
|
|
||||||
# Open the terminal
|
# Open the terminal
|
||||||
my $ws_proto = lc($ENV{'HTTPS'}) eq 'on' ? 'wss' : 'ws';
|
my $url = &get_miniserv_websocket_url($port, $config{'host'}, $module_name);
|
||||||
my $http_host_conf = &trim($config{'host'});
|
|
||||||
if ($http_host_conf) {
|
|
||||||
if ($http_host_conf !~ /^wss?:\/\//) {
|
|
||||||
$http_host_conf = "$ws_proto://$http_host_conf";
|
|
||||||
}
|
|
||||||
$http_host_conf =~ s/[\/]+$//g;
|
|
||||||
}
|
|
||||||
my $http_host = $http_host_conf || "$ws_proto://$ENV{'HTTP_HOST'}";
|
|
||||||
my $url = "$http_host/$module_name/ws-$port";
|
|
||||||
my $canvasAddon = $termlinks->{'js'}[3];
|
my $canvasAddon = $termlinks->{'js'}[3];
|
||||||
my $webGLAddon = $termlinks->{'js'}[4];
|
my $webGLAddon = $termlinks->{'js'}[4];
|
||||||
my $term_script = <<EOF;
|
my $term_script = <<EOF;
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ index_eproxy=The Terminal module cannot be used when accessing Webmin via anothe
|
|||||||
|
|
||||||
acl_user=Run shell as Unix user
|
acl_user=Run shell as Unix user
|
||||||
acl_sameuser=Same as Webmin login
|
acl_sameuser=Same as Webmin login
|
||||||
|
acl_sudoenforce=Enforce <em>sudo</em>-only privileges
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
desc=Terminal
|
desc=Terminal
|
||||||
name=xterm
|
name=xterm
|
||||||
longdesc=Access the shell on your system without the need for a separate SSH client, using Xterm.js over Webmin WebSockets
|
longdesc=Access the shell on your system without the need for a separate SSH client, using Xterm.js over Webmin WebSockets
|
||||||
|
websockets=1
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ my @uinfo = getpwnam($user);
|
|||||||
my ($uid, $gid);
|
my ($uid, $gid);
|
||||||
if ($user ne "root" && !$<) {
|
if ($user ne "root" && !$<) {
|
||||||
if (!@uinfo) {
|
if (!@uinfo) {
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
die "User $user does not exist!";
|
die "User $user does not exist!";
|
||||||
}
|
}
|
||||||
$uid = $uinfo[2];
|
$uid = $uinfo[2];
|
||||||
@@ -83,11 +83,11 @@ my ($shellfh, $pid) = &proc::pty_process_exec($shellexec, $uid, $gid, $shelllogi
|
|||||||
&reset_environment();
|
&reset_environment();
|
||||||
my $shcmd = "'$shellexec".($shelllogin ? " $shelllogin" : "")."'";
|
my $shcmd = "'$shellexec".($shelllogin ? " $shelllogin" : "")."'";
|
||||||
if (!$pid) {
|
if (!$pid) {
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
die "Failed to run shell with $shcmd\n";
|
die "Failed to run shell with $shcmd\n";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print STDERR "Running shell $shcmd with pid $pid\n";
|
&error_stderr("Running shell $shcmd for user $user with pid $pid");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Detach from controlling terminal
|
# Detach from controlling terminal
|
||||||
@@ -99,19 +99,19 @@ close(STDIN);
|
|||||||
|
|
||||||
# Clean up when socket is terminated
|
# Clean up when socket is terminated
|
||||||
$SIG{'ALRM'} = sub {
|
$SIG{'ALRM'} = sub {
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
die "timeout waiting for connection";
|
die "timeout waiting for connection";
|
||||||
};
|
};
|
||||||
alarm(60);
|
alarm(60);
|
||||||
print STDERR "listening on port $port\n";
|
&error_stderr("Listening on port $port");
|
||||||
my ($wsconn, $shellbuf);
|
my ($wsconn, $shellbuf);
|
||||||
Net::WebSocket::Server->new(
|
Net::WebSocket::Server->new(
|
||||||
listen => $port,
|
listen => $port,
|
||||||
on_connect => sub {
|
on_connect => sub {
|
||||||
my ($serv, $conn) = @_;
|
my ($serv, $conn) = @_;
|
||||||
print STDERR "got websockets connection\n";
|
&error_stderr("WebSocket connection established");
|
||||||
if ($wsconn) {
|
if ($wsconn) {
|
||||||
print STDERR "Unexpected second connection to the same port\n";
|
&error_stderr("Unexpected second connection to the same port");
|
||||||
$conn->disconnect();
|
$conn->disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ Net::WebSocket::Server->new(
|
|||||||
$key =~ s/\s//g;
|
$key =~ s/\s//g;
|
||||||
$dsess =~ s/\s//g;
|
$dsess =~ s/\s//g;
|
||||||
if (!$key || !$dsess || $key ne $dsess) {
|
if (!$key || !$dsess || $key ne $dsess) {
|
||||||
print STDERR "Key $key does not match session ID $dsess\n";
|
&error_stderr("Key $key does not match session ID $dsess");
|
||||||
$conn->disconnect();
|
$conn->disconnect();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -140,7 +140,7 @@ Net::WebSocket::Server->new(
|
|||||||
# Check for resize escape sequence explicitly
|
# Check for resize escape sequence explicitly
|
||||||
if ($msg =~ /^\\033\[8;\((\d+)\);\((\d+)\)t$/) {
|
if ($msg =~ /^\\033\[8;\((\d+)\);\((\d+)\)t$/) {
|
||||||
my ($rows, $cols) = ($1, $2);
|
my ($rows, $cols) = ($1, $2);
|
||||||
print STDERR "got resize to $rows $cols\n";
|
&error_stderr("Got resize to $rows $cols");
|
||||||
eval {
|
eval {
|
||||||
$shellfh->set_winsize($rows, $cols);
|
$shellfh->set_winsize($rows, $cols);
|
||||||
};
|
};
|
||||||
@@ -153,14 +153,14 @@ Net::WebSocket::Server->new(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!syswrite($shellfh, $msg, length($msg))) {
|
if (!syswrite($shellfh, $msg, length($msg))) {
|
||||||
print STDERR "write to shell failed : $!\n";
|
&error_stderr("Write to shell failed : $!");
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disconnect => sub {
|
disconnect => sub {
|
||||||
print STDERR "websocket connection closed\n";
|
&error_stderr("WebSocket connection closed");
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
kill('KILL', $pid) if ($pid);
|
kill('KILL', $pid) if ($pid);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -172,8 +172,8 @@ Net::WebSocket::Server->new(
|
|||||||
my $buf;
|
my $buf;
|
||||||
my $ok = sysread($shellfh, $buf, 1024);
|
my $ok = sysread($shellfh, $buf, 1024);
|
||||||
if ($ok <= 0) {
|
if ($ok <= 0) {
|
||||||
print STDERR "end of output from shell\n";
|
&error_stderr("End of output from shell");
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
if ($wsconn) {
|
if ($wsconn) {
|
||||||
@@ -185,6 +185,6 @@ Net::WebSocket::Server->new(
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
)->start;
|
)->start;
|
||||||
print STDERR "exited websockets server\n";
|
&error_stderr("Exited WebSocket server");
|
||||||
&remove_miniserv_websocket($port);
|
&remove_miniserv_websocket($port, $module_name);
|
||||||
&cleanup_miniserv_websockets([$port]);
|
&cleanup_miniserv_websockets([$port], $module_name);
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
# allocate_miniserv_websocket()
|
|
||||||
# Allocate a new websocket and
|
|
||||||
# stores it miniserv.conf file
|
|
||||||
sub allocate_miniserv_websocket
|
|
||||||
{
|
|
||||||
# Find ports already in use
|
|
||||||
&lock_file(&get_miniserv_config_file());
|
|
||||||
my %miniserv;
|
|
||||||
&get_miniserv_config(\%miniserv);
|
|
||||||
my %inuse;
|
|
||||||
foreach my $k (keys %miniserv) {
|
|
||||||
if ($k =~ /^websockets_/ && $miniserv{$k} =~ /port=(\d+)/) {
|
|
||||||
$inuse{$1} = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pick a port and configure Webmin to proxy it
|
|
||||||
my $port = $config{'base_port'} || 555;
|
|
||||||
while(1) {
|
|
||||||
if (!$inuse{$port}) {
|
|
||||||
&open_socket("127.0.0.1", $port, my $fh, \$err);
|
|
||||||
last if ($err);
|
|
||||||
close($fh);
|
|
||||||
}
|
|
||||||
$port++;
|
|
||||||
}
|
|
||||||
my $wspath = "/$module_name/ws-".$port;
|
|
||||||
my $now = time();
|
|
||||||
$miniserv{'websockets_'.$wspath} = "host=127.0.0.1 port=$port wspath=/ user=$remote_user time=$now";
|
|
||||||
&put_miniserv_config(\%miniserv);
|
|
||||||
&unlock_file(&get_miniserv_config_file());
|
|
||||||
&reload_miniserv();
|
|
||||||
return $port;
|
|
||||||
}
|
|
||||||
|
|
||||||
# remove_miniserv_websocket(port)
|
|
||||||
# Remove old websocket
|
|
||||||
# from miniserv.conf
|
|
||||||
sub remove_miniserv_websocket
|
|
||||||
{
|
|
||||||
my ($port) = @_;
|
|
||||||
my %miniserv;
|
|
||||||
if ($port) {
|
|
||||||
&lock_file(&get_miniserv_config_file());
|
|
||||||
&get_miniserv_config(\%miniserv);
|
|
||||||
my $wspath = "/$module_name/ws-".$port;
|
|
||||||
if ($miniserv{'websockets_'.$wspath}) {
|
|
||||||
delete($miniserv{'websockets_'.$wspath});
|
|
||||||
&put_miniserv_config(\%miniserv);
|
|
||||||
&reload_miniserv();
|
|
||||||
}
|
|
||||||
&unlock_file(&get_miniserv_config_file());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# cleanup_miniserv_websockets([&skip-ports])
|
|
||||||
# Called by scheduled status collection to remove any
|
|
||||||
# websockets in miniserv.conf that are no longer used
|
|
||||||
sub cleanup_miniserv_websockets
|
|
||||||
{
|
|
||||||
my ($skip) = @_;
|
|
||||||
$skip ||= [ ];
|
|
||||||
&lock_file(&get_miniserv_config_file());
|
|
||||||
my %miniserv;
|
|
||||||
&get_miniserv_config(\%miniserv);
|
|
||||||
my $now = time();
|
|
||||||
my @clean;
|
|
||||||
foreach my $k (keys %miniserv) {
|
|
||||||
$k =~ /^websockets_\/$module_name\/ws-(\d+)$/ || next;
|
|
||||||
my $port = $1;
|
|
||||||
next if (&indexof($port, @$skip) >= 0);
|
|
||||||
my $when = 0;
|
|
||||||
if ($miniserv{$k} =~ /time=(\d+)/) {
|
|
||||||
$when = $1;
|
|
||||||
}
|
|
||||||
if ($now - $when > 60) {
|
|
||||||
# Has been open for a while, check if the port is still in use?
|
|
||||||
my $err;
|
|
||||||
&open_socket("127.0.0.1", $port, my $fh, \$err);
|
|
||||||
if ($err) {
|
|
||||||
# Closed now, can clean up
|
|
||||||
push(@clean, $k);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Still active
|
|
||||||
close($fh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (@clean) {
|
|
||||||
foreach my $k (@clean) {
|
|
||||||
delete($miniserv{$k});
|
|
||||||
}
|
|
||||||
&put_miniserv_config(\%miniserv);
|
|
||||||
&reload_miniserv();
|
|
||||||
}
|
|
||||||
&unlock_file(&get_miniserv_config_file());
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
||||||
@@ -4,7 +4,6 @@ BEGIN { push(@INC, ".."); };
|
|||||||
use WebminCore;
|
use WebminCore;
|
||||||
&init_config();
|
&init_config();
|
||||||
our %access = &get_module_acl();
|
our %access = &get_module_acl();
|
||||||
do "$module_root_directory/websockets-lib-funcs.pl";
|
|
||||||
|
|
||||||
# config_pre_load(mod-info-ref, [mod-order-ref])
|
# config_pre_load(mod-info-ref, [mod-order-ref])
|
||||||
# Check if some config options are conditional,
|
# Check if some config options are conditional,
|
||||||
@@ -12,22 +11,13 @@ do "$module_root_directory/websockets-lib-funcs.pl";
|
|||||||
sub config_pre_load
|
sub config_pre_load
|
||||||
{
|
{
|
||||||
my ($modconf_info, $modconf_order) = @_;
|
my ($modconf_info, $modconf_order) = @_;
|
||||||
|
|
||||||
if ($ENV{'HTTP_X_REQUESTED_WITH'} eq "XMLHttpRequest") {
|
if ($ENV{'HTTP_X_REQUESTED_WITH'} eq "XMLHttpRequest") {
|
||||||
|
# Size is not supported in Authentic, because resize works flawlessly
|
||||||
# Process forbidden keys
|
# and making it work would just add addition complexity for no good
|
||||||
my @forbidden_keys;
|
# reason
|
||||||
|
delete($modconf_info->{'size'});
|
||||||
# Size is not supported in Authentic, because resize works flawlessly and
|
@{$modconf_order} = grep { $_ ne 'size' } @{$modconf_order}
|
||||||
# making it work would just add addition complexity for no good reason
|
if ($modconf_order);
|
||||||
push(@forbidden_keys, 'size');
|
|
||||||
|
|
||||||
# Remove forbidden from display
|
|
||||||
foreach my $fkey (@forbidden_keys) {
|
|
||||||
delete($modconf_info->{$fkey});
|
|
||||||
@{$modconf_order} = grep { $_ ne $fkey } @{$modconf_order}
|
|
||||||
if ($modconf_order);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user