initial commit of file from CVS for e-smith-viewlogfiles on Wed 12 Jul 09:12:02 BST 2023

This commit is contained in:
Brian Read
2023-07-12 09:12:02 +01:00
parent 1ef117a658
commit f5ebdcc806
12 changed files with 1341 additions and 2 deletions

View File

@@ -0,0 +1 @@
configuration

View File

@@ -0,0 +1,127 @@
<!-- vim: ft=xml ts=8 sw=4 noet:
-->
<lexicon lang="en-us">
<entry>
<base>View log files</base>
<trans>View log files</trans>
</entry>
<entry>
<base>FIRSTPAGE_DESC</base>
<trans>
<![CDATA[
This panel allows you to view or download the log files generated
by the services running on your server.
]]>
</trans>
</entry>
<entry>
<base>LOG_FILE_SELECT_DESC</base>
<trans>Choose a log file to view</trans>
</entry>
<entry>
<base>FILTER_PATTERN_DESC</base>
<trans>
<![CDATA[
<p>You may optionally specify a filter pattern to display only the
lines from the log file which match this pattern. If you leave
this field blank, all available lines of the log file will be
displayed. Note that this option is not used if you download the
logfile.</p>
]]>
</trans>
</entry>
<entry>
<base>FILTER_PATTERN_LABEL</base>
<trans>Filter Pattern (optional)</trans>
</entry>
<entry>
<base>MATCH_PATTERN_DESC</base>
<trans>
<![CDATA[
<p>You may also optionally specify a highlight pattern to mark in bold
any lines from the log file which match the highlight pattern. The
highlight pattern is applied to any lines which have already
matched the filter pattern. Note that this option is not used if
you download the logfile.</p>
]]>
</trans>
</entry>
<entry>
<base>MATCH_PATTERN_LABEL</base>
<trans>Highlight Pattern (optional)</trans>
</entry>
<entry>
<base>END_DESC</base>
<trans>
Please note that it may take quite some time to generate these
reports.
</trans>
</entry>
<entry>
<base>VIEW</base>
<trans>View log file</trans>
</entry>
<entry>
<base>LOG_FILE_EMPTY</base>
<trans>Log file "{$filename}" is empty!</trans>
</entry>
<entry>
<base>VIEWING_TIME</base>
<trans>Viewed at {$time}.</trans>
</entry>
<entry>
<base>MATCH_HEADER</base>
<trans>Displaying lines matching: "{$matchPattern}".</trans>
</entry>
<entry>
<base>HIGHLIGHT_HEADER</base>
<trans>Highlighting lines matching: "{$highlightPattern}".</trans>
</entry>
<entry>
<base>NO_MATCHING_LINES</base>
<trans>No matching lines displayed.</trans>
</entry>
<entry>
<base>FILENAME_ERROR</base>
<trans>
<![CDATA[
<p>Error while specifying log file name.</p>
<p>Invalid report type "{$filename}".</p>
]]>
</trans>
</entry>
<entry>
<base>REFRESH</base>
<trans>Refresh this logfile</trans>
</entry>
<entry>
<base>OP_DESC</base>
<trans><![CDATA[
<p>You must choose between viewing the logfile in your browser, or
downloading the logfile to your computer. If the logfile is
particularly large, you may wish to download it instead of
attempting to open it in your browser, as this is a problem for
some web browsers.</p>
]]>
</trans>
</entry>
<entry>
<base>OP_LABEL</base>
<trans>Operation</trans>
</entry>
<entry>
<base>DOWNLOAD_PAGE_DESC</base>
<trans><![CDATA[
<p>Your logfile download is now prepared. It will proceed as soon
as you click on the &quot;Next&quot; button below, and instruct
your browser to accept the download via the pop-up window that
will appear.</p>
]]>
</trans>
</entry>
<entry>
<base>DOWNLOAD_FILE</base>
<trans>Preparing to download the logfile {$logfile}.</trans>
</entry>
</lexicon>

View File

@@ -0,0 +1,477 @@
#! /usr/bin/perl -wT
#----------------------------------------------------------------------
# heading : Administration
# description : View log files
# navigation : 4000 4400
#
# $Id: viewlogfiles,v 1.31 2005/08/25 21:08:41 charlieb Exp $
#----------------------------------------------------------------------
# copyright (C) 1999-2007 Mitel Networks Corporation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#----------------------------------------------------------------------
use strict;
use esmith::FormMagick;
use esmith::ConfigDB;
use esmith::cgi;
use Time::TAI64;
use File::Basename;
use HTML::Entities;
use CGI;
use constant TRUE => 1;
use constant FALSE => 0;
my $viewlogfiles = esmith::ConfigDB->open->get('viewlogfiles');
my $fm = esmith::FormMagick->new();
my $q = CGI->new();
# Save this operation preference for later.
my $operation = $q->param('operation');
$viewlogfiles->merge_props('DefaultOperation', $operation)
if $operation;
if ($operation and $operation eq 'download')
{
perform_download($fm, $q);
}
else
{
#$fm->debug(1);
$fm->display();
}
=pod
=head1 NAME
viewlogfiles -- An interface to system log files, allowing for pattern
filtering and highlighting.
=head2 DESCRIPTION
This screen allows the administrator to flexibly view various system log files.
=begin testing
use esmith::FormMagick::Tester;
use esmith::TestUtils;
use esmith::ConfigDB;
use esmith::AccountsDB;
my $panel = $Original_File;
my $ua = esmith::FormMagick::Tester->new();
my $c = esmith::ConfigDB->open();
my $a = esmith::AccountsDB->open();
is (mode($panel), '4755', "Check permissions on script");
ok ($ua->get_panel($panel), "ABOUT TO RUN L10N TESTS");
is ($ua->{status}, 200, "200 OK");
ok ($ua->set_language("en-us"), "Set language to English");
ok ($ua->get_panel($panel), "Get panel");
is ($ua->{status}, 200, "200 OK");
like($ua->{content}, qr/View log files/, "Saw translated form title");
# View the messages log:
$ua->field("filename" => "messages");
$ua->field(highlightPattern => "");
$ua->field(matchPattern => "");
ok ($ua->click("View log file"), "Click View log file");
is ($ua->{status}, 200, "200 OK");
like($ua->{content}, qr/Viewed at/, "Saw validation messages");
# View the messages log and filter all output:
ok ($ua->get_panel($panel), "Get panel");
$ua->field("filename" => "messages");
$ua->field(highlightPattern => "gibberish-gibberish");
$ua->field(matchPattern => "gibberish-gibberish");
ok ($ua->click("View log file"), "Click View log file");
is ($ua->{status}, 200, "200 OK");
like($ua->{content}, qr/No matching lines/, "Saw validation messages");
=end testing
=cut
#------------------------------------------------------------
# subroutine to display initial form
#------------------------------------------------------------
our %logfiles = ();
sub timestamp2local
{
$_ = shift;
if (/^(\@[0-9a-f]{24})(.*)/s)
{
return Time::TAI64::tai64nlocal($1) . $2;
}
elsif (/^([0-9]{10}\.[0-9]{3})(.*)/s)
{
return localtime($1) . $2;
}
return $_;
}
sub findlogFiles
{
my $fm = shift;
my $q = $fm->{cgi};
use File::Find;
sub findlogfiles
{
my $path = $File::Find::name;
if (-f)
{
# Remove leading /var/log/messages
$path =~ s:^/var/log/::;
# don't bother to collect files known to be non-text
# or not log files
foreach (qw(
lastlog
btmp$
wtmp
lock
(?<!qpsmtpd/)state
httpd/ssl_mutex.\d*
httpd/ssl_scache.pag
httpd/ssl_scache.dir
\/config$
))
{
return if $path =~ /$_/;
}
my ($file_base, $file_path, $file_type) = fileparse($path);
if ( $file_base =~ /@.*/ )
{
$logfiles{$path} = $file_path . timestamp2local($file_base);
}
else
{
$logfiles{$path} = $path;
}
}
}
# Now go and find all the files under /var/log
find({wanted => \&findlogfiles, no_chdir => 1}, '/var/log');
return \%logfiles;
}
#------------------------------------------------------------
# subroutine to perform actions and display result
#------------------------------------------------------------
sub performAndShowResult ($)
{
my $fm = shift;
my $q = $fm->{cgi};
#------------------------------------------------------------
# Verify the arguments and untaint the variables (see Camel
# book, "Detecting and laundering tainted data", pg. 358)
#------------------------------------------------------------
my $filename = $q->param ('filename');
if ($filename =~ /^([\S\s]+)$/)
{
$filename = $1;
}
elsif ($filename =~ /^$/)
{
$filename = "messages";
}
else
{
print $fm->localise("FILENAME_ERROR", { filename => "$filename" } );
return;
}
my $matchPattern = $q->param ('matchPattern');
if ($matchPattern =~ /^(\S+)$/)
{
$matchPattern = $1;
}
else
{
$matchPattern = ".";
}
my $highlightPattern = $q->param ('highlightPattern');
if ($highlightPattern =~ /^(\S+)$/)
{
$highlightPattern = $1;
}
else
{
$highlightPattern = '';
}
#------------------------------------------------------------
# Looks good; go ahead and generate the report.
#------------------------------------------------------------
my $fullpath = "/var/log/$filename";
if (-z $fullpath)
{
print $fm->localise("LOG_FILE_EMPTY", { filename => "$filename" } );
return;
}
print "$fullpath: \n";
print $fm->localise("VIEWING_TIME",
{ time => $fm->gen_locale_date_string() } );
unless ( $matchPattern eq '.' )
{
print "<p>\n";
print $fm->localise("MATCH_HEADER", { matchPattern => "$matchPattern" } );
}
if ( $highlightPattern )
{
print "<p>\n";
print $fm->localise("HIGHLIGHT_HEADER", { highlightPattern => "$highlightPattern" } );
}
if ($filename =~ /\.gz$/)
{
my $pid = open(LOGFILE, "-|");
die "Couldn't fork: $!" unless defined $pid;
unless ($pid)
{
# Child
exec("/bin/zcat", $fullpath)
|| die "Can't exec zcat: $!";
# NOTREACHED
}
}
else
{
open(LOGFILE, "$fullpath");
}
my $somethingMatched = 0;
my $fileEmpty = 1;
print "<PRE>";
while(<LOGFILE>)
{
$fileEmpty = 0;
next unless /$matchPattern/;
$somethingMatched = 1;
$_ = timestamp2local($_);
$_ = HTML::Entities::encode_entities($_);
($highlightPattern && /$highlightPattern/)
? print "<b>$_</b>"
: print;
}
print "</PRE>";
if ($fileEmpty)
{
print "<p>\n";
print $fm->localise("LOG_FILE_EMPTY");
}
else
{
unless ($somethingMatched)
{
print "<p>\n";
print $fm->localise("NO_MATCHING_LINES");
}
}
close LOGFILE;
print $q->table({-width => '100%'},
$q->Tr($q->th({-class => 'sme-layout'},
$q->a( { -href => "viewlogfiles?page=0&Next=viewLog" .
"&filename=$filename&matchPattern=$matchPattern" .
"&highlightPattern=$highlightPattern" .
"&operation=view",
-class => 'button-like'},
$fm->localise('REFRESH')))));
return;
}
sub print_viewlog_buttons
{
my $self = shift;
my $q = $self->{cgi};
my $filename = $q->param('filename');
my $matchPattern = $q->param('matchPattern');
my $highlightPattern = $q->param('highlightPattern');
print $q->table({-width => '100%'},
$q->Tr({-valign => 'center'},
$q->th({-class => 'sme-layout', -valign => 'center'},
$q->a( { -href => "viewlogfiles?page=0&Next=viewLog" .
"&filename=$filename&matchPattern=$matchPattern" .
"&highlightPattern=$highlightPattern" .
"&skip_header=1",
-class => 'button-like'},
$self->localise('DOWNLOAD')),
$q->submit( {-value => $self->localise('VIEW') } ))));
return undef;
}
sub perform_download
{
my $fm = shift;
my $q = shift;
my $filename = $q->param("filename");
my $fullpath = "/var/log/$filename";
# Save this information for later.
my $operation = $q->param('operation');
$viewlogfiles->merge_props('DefaultOperation', $operation);
# If the client is on windows, we must handle this a little differently.
my $win32 = FALSE;
my $mac = FALSE;
my $agent = $ENV{HTTP_USER_AGENT} || "";
if ($agent =~ /win32|windows/i)
{
$win32 = TRUE;
}
elsif ($agent =~ /mac/i)
{
$mac = TRUE;
}
# Check for errors first. Once we start sending the file it's too late to
# report them.
my $error = "";
unless (-f $fullpath)
{
$error = $fm->localise("ERR_NOEXIST_FILE");
}
local *FILE;
open(FILE, "<$fullpath")
or $error = $fm->localise("ERR_NOOPEN_FILE");
# Put other error checking here.
if ($error)
{
# FIXME: Add the header and footer template references here.
print <<"EOF";
Content-Type: text/html
$error
EOF
return undef;
}
# Fix the filename, as it might have a directory prefixed to it.
if ($filename =~ m#/#)
{
$filename = (split /\//, $filename)[-1];
}
# Otherwise, send the file. Start with the headers.
# Note: The Content-disposition must be attachment, or IE will view the
# file inline like it's told. It ignores the Content-type, but it likes
# the Content-disposition (an officially unsupported header) for some
# reason. Yay Microsoft.
print <<"EOF";
Expires: 0
Content-type: application/octet-stream
Content-disposition: attachment; filename=$filename
EOF
# And send the file.
my $nl = "\n";
if ($win32) { $nl = "\r\n" }
elsif ($mac) { $nl = "\r" }
while (my $line = <FILE>)
{
chomp $line;
print timestamp2local($line) . $nl;
}
close(FILE);
return undef;
}
sub show_operation_widget
{
my $self = shift;
my $q = $self->{cgi};
my $description = $self->localise('OP_DESC');
my $label = $self->localise('OP_LABEL');
my $defaultop = $viewlogfiles->prop('DefaultOperation');
my $select = '<select name="operation" size="1">' . "\n";
if ($defaultop eq 'view')
{
$select .= '<option value="view" selected>' .
$self->localise("VIEW") . '</option>' . "\n";
$select .= '<option value="download">' .
$self->localise("DOWNLOAD") . '</option>' . "\n";
}
else
{
$select .= '<option value="view">' .
$self->localise("VIEW") . '</option>' . "\n";
$select .= '<option value="download" selected>' .
$self->localise("DOWNLOAD") . '</option>' . "\n";
}
$select .= '</select>' . "\n";
print $q->Tr($q->td({-colspan => 2}, $description));
print $q->Tr($q->td({-class => 'sme-noborders-label'}, $label),
$q->td({-class => 'sme-noborders-content'}, $select));
return undef;
}
__DATA__
<form title="View log files" header="/etc/e-smith/web/common/head.tmpl" footer="/etc/e-smith/web/common/foot.tmpl">
<page name="Initial"
pre-event="print_status_message()">
<description>FIRSTPAGE_DESC</description>
<field type="select" id="filename" options="findlogFiles()"
value="messages">
<label>LOG_FILE_SELECT_DESC</label>
</field>
<field type="text" id="matchPattern">
<description>FILTER_PATTERN_DESC</description>
<label>FILTER_PATTERN_LABEL</label>
</field>
<field type="text" id="highlightPattern">
<description>MATCH_PATTERN_DESC</description>
<label>MATCH_PATTERN_LABEL</label>
</field>
<subroutine src="show_operation_widget()" />
<field type="literal">
<description>END_DESC</description>
</field>
<subroutine src="print_button('NEXT')" />
</page>
<page name="viewLog" pre-event="turn_off_buttons()">
<subroutine src="performAndShowResult()" />
</page>
</form>