#! /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()
    {
        $fileEmpty = 0;
        next unless /$matchPattern/;
        $somethingMatched = 1;

        $_ = timestamp2local($_);
        $_ = HTML::Entities::encode_entities($_);
        ($highlightPattern && /$highlightPattern/)
        ? print "$_"
        : print;
    }
    print "
"; if ($fileEmpty) { 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 = ) { 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 = '' . "\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__

FIRSTPAGE_DESC FILTER_PATTERN_DESC MATCH_PATTERN_DESC END_DESC