smeserver-viewlogfiles/root/etc/e-smith/web/functions/viewlogfiles

478 lines
13 KiB
Perl
Executable File

#! /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>