#! /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 (? \&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 "
\n"; print $fm->localise("MATCH_HEADER", { matchPattern => "$matchPattern" } ); } if ( $highlightPattern ) { print "
\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 "
"; while("; if ($fileEmpty) { print ") { $fileEmpty = 0; next unless /$matchPattern/; $somethingMatched = 1; $_ = timestamp2local($_); $_ = HTML::Entities::encode_entities($_); ($highlightPattern && /$highlightPattern/) ? print "$_" : print; } print "
\n"; print $fm->localise("LOG_FILE_EMPTY"); } else { unless ($somethingMatched) { print "
\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 =