#! /usr/bin/perl # $Id$ # Copyright (C) 2001-2002 MandrakeSoft # Yves Duret # some code is Copyright: (C) 1999, Michael T. Babcock # # 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, 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 lib qw(/usr/lib/libDrakX); use standalone; #- warning, standalone must be loaded very first, for 'explanations' use common; use interactive; use any; use my_gtk qw(:helpers :wrappers); $::isInstall and die "Not supported during install.\n"; my $in = 'interactive'->vnew('su', 'default'); my $cron_hourly = "/etc/cron.hourly/logdrake_service"; if ($::isEmbedded) { print "EMBED\n"; print "parent XID\t$::XID\n"; print "mcc pid\t$::CCPID\n"; } #- parse arguments list. for (@ARGV) { /^--explain=(.*)$/ and do { $::isExplain = ($::Explain) = $1; $::isFile = 1; $::File = "/var/log/explanations"; next }; /^--file=(.*)$/ and do { $::isFile = ($::File) = $1; next }; /^--word=(.*)$/ and do { $::isWord = ($::Word) = $1; next }; /^--alert$/ and do { alert_config(); quit() }; } $::isTail = 1 if ($::isFile); $| = 1 if ($::isTail); my $h = chomp_(`hostname -s`); my $my_win = my_gtk->new('logdrake'); unless ($::isEmbedded) { $my_win->{rwindow}->set_title(N("logdrake")); $my_win->{window}->border_width (5); $my_win->{rwindow}->set_policy(1, 1, 1); } $my_win->{window}->signal_connect(delete_event => \&quit); #$my_win->{window}->set_default_size(540,460); my $cal = gtkset_sensitive(new Gtk::Calendar(),0); my (undef,undef,undef,$mday) = localtime(time); $cal->select_day($mday); my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my $cal_mode = 0; my $cal_butt = gtksignal_connect(new Gtk::CheckButton(N("Show only for the selected day")), clicked =>sub{ $cal_mode =!$cal_mode; gtkset_sensitive($cal,$cal_mode) }); ### menus definition # the menus are not shown # but they provides shiny shortcut like C-q my @menu_items = ( { path => N("/_File"), type => '' }, { path => N("/File/_New"), accelerator => N("N"), callback => \&print_hello }, { path => N("/File/_Open"), accelerator => N("O"),callback => \&print_hello }, { path => N("/File/_Save"), accelerator => N("S"),callback => \&save }, { path => N("/File/Save _As") }, { path => N("/File/-"),type => '' }, { path => N("/File/_Quit"), accelerator => N("Q"), callback => \&quit }, { path => N("/_Options"), type => '' }, { path => N("/Options/Test") }, { path => N("/_Help"),type => '' }, { path => N("/Help/_About...") } ); my $menubar = ugtk::create_factory_menu($my_win->{rwindow}, @menu_items) unless $::isEmbedded; ######### menus end ########## font and colors my $n = Gtk::Gdk::Font->fontset_load(N("-misc-fixed-medium-r-*-*-*-100-*-*-*-*-*-*,*")); my $b = Gtk::Gdk::Font->fontset_load(N("-misc-fixed-bold-r-*-*-*-100-*-*-*-*-*-*,*")); #$black = "\033[30m"; #$red = "\033[31m"; #$green = "\033[32m"; #$yellow = "\033[33m"; #$blue = "\033[34m"; #$magenta = "\033[35m"; #$purple = "\033[35m"; #$cyan = "\033[36m"; #$white = "\033[37m"; #$darkgray = "\033[30m"; #$col_norm = "\033[00m"; #$col_background = "\033[07m"; #$col_brighten = "\033[01m"; #$col_underline = "\033[04m"; #$col_blink = "\033[05m"; my $white = my_gtk::gtkcolor(50400, 655, 20000); my $black = my_gtk::gtkcolor(0, 0, 0); my $red = my_gtk::gtkcolor(0xFFFF, 655, 655); my $green = my_gtk::gtkcolor(0x0, 0x9898,0x0); my $yellow = my_gtk::gtkcolor(0xFFFF, 0xD7D7, 0); my $blue = my_gtk::gtkcolor(655, 655, 0xFFFF); my $magenta = my_gtk::gtkcolor(0xFFFF, 655, 0xFFFF); my $purple = my_gtk::gtkcolor(0xA0A0, 0x2020, 0xF0F0); my $cyan = my_gtk::gtkcolor(0x0, 0x9898, 0x9898); my $darkgray = my_gtk::gtkcolor(0x2F2F, 0x4F4F, 0x4F4F); # Define global terms: # Define good notables: my @word_good = ("starting\n", "Freeing", "Detected", "starting.", "accepted.\n", "authenticated.\n", "Ready", "active", "reloading", "saved;", "restarting", "ONLINE\n"); my @word_warn = ("dangling", "closed.\n", "Assuming", "root", "root\n", "exiting\n", "missing", "Ignored", "adminalert:", "deleting", "OFFLINE\n"); my @word_bad = ("bad"); my @word_note = ("LOGIN", "DHCP_OFFER", "optimized", "reset:", "unloaded", "disconnected", "connect", "Successful", "registered\n"); my @line_good = ("up", "DHCP_ACK", "Cleaned", "Initializing", "Starting", "success", "successfully", "alive", "found", "ONLINE\n"); my @line_warn = ("warning:", "WARNING:", "invalid", "obsolete", "bad", "Password", "detected", "timeout", "timeout:", "attackalert:", "wrong", "Lame", "FAILED", "failing", "unknown", "obsolete", "stopped.\n", "terminating.", "disabled\n", "disabled", "Lost"); my @line_bad = ("DENY", "lost", "shutting", "dead", "DHCP_NAK", "failure;", "Unable", "inactive", "terminating", "refused", "rejected", "down", "OFFLINE\n", "error\n", "ERROR\n", "ERROR:", "error", "ERROR", "error:", "failed:"); # Define specifics: my @daemons = ("named"); # Now define what we want to use when: my $col_good = $green; my $col_warn = $yellow; my $col_bad = $red; my $col_note = $purple; my $col = $cyan; ######### font and colors end my %files = ( "auth" => { file => "/var/log/auth.log", desc => N("Authentication") }, "user" => { file => "/var/log/user.log", desc => N("User") }, "messages" => { file => "/var/log/messages", desc => N("Messages") }, "syslog" => { file => "/var/log/syslog", desc => N("Syslog") }, "explanations" => { file => "/var/log/explanations", desc => N("Mandrake Tools Explanations") } ); my $yy = gtkset_sensitive(gtksignal_connect(new Gtk::Button(N("search")) , clicked => \&search),0); my $log_text = new Gtk::Text(undef, undef); my $refcount_search; #### far from window no strict 'refs'; gtkadd($my_win->{window}, gtkpack_(new Gtk::VBox(0,0), if_(!$::isExplain && !$::isEmbedded, 0, N("A tool to monitor your logs")), if_(!$::isFile, 0, gtkadd(new Gtk::Frame(N("Settings")), gtkpack__(new Gtk::VBox(0,2), gtkpack__(new Gtk::VBox(0,2), # N("Show lines"), gtkpack__(new Gtk::HBox(0,0), " " . N("matching") . " ", my $e_yes = new Gtk::Entry(), " " . N("but not matching") . " ", my $e_no = new Gtk::Entry() ) ), gtkpack_(new Gtk::HBox(0,0), 1, gtkadd(gtkset_border_width(new Gtk::Frame(N("Choose file")),2), gtkpack (gtkset_border_width(new Gtk::VBox(0,0),0), map { ${ "b_". $_ } = gtksignal_connect(new Gtk::CheckButton($files{ $_ }{desc}), clicked => sub{ $refcount_search++; gtkset_sensitive($yy,$refcount_search) }) } keys %files, ) ), 0, gtkadd(gtkset_border_width(new Gtk::Frame(N("Calendar")),2), gtkpack__(gtkset_border_width(new Gtk::VBox(0,0),5), $cal_butt, $cal ) ) ), $yy, ) ) ), !$::isExplain ? (1, gtkadd(new Gtk::Frame(N("Content of the file")), createScrolledWindow($log_text) )) : (1, createScrolledWindow($log_text)), if_(!$::isExplain, 0, gtkadd (gtkset_border_width(gtkset_layout(new Gtk::HButtonBox,-end), 5), if_(!$::isFile, gtksignal_connect(new Gtk::Button (N("Mail alert")), clicked => sub { eval { alert_config() }; if ($@ =~ /wizcancel/) { $::Wizard_no_previous = 1; $::Wizard_no_cancel = 1; $::WizardWindow->destroy if defined $::WizardWindow; undef $::WizardWindow; } })), gtksignal_connect(new Gtk::Button (N("Save")), clicked => \&save), gtksignal_connect(new Gtk::Button ($::isEmbedded ? N("Cancel") : N("Quit")), clicked => \&quit) ) ) ) ); use strict; $::isFile and gtkset_usize($log_text,400,500); $my_win->{window}->realize; $my_win->{window}->show_all(); search() if ($::isFile); $my_win->main; sub quit { my_gtk->exit(0) } #------------------------------------------------------------- # search functions #------------------------------------------------------------- sub search { $log_text->backward_delete($log_text->get_length()); $log_text->freeze(); if ($::isFile) { parse_file($::File); } else { foreach (keys %files) { parse_file($files{ $_ }{file}) if ${ $::{ "b_". $_ }}->active }; } $log_text->thaw(); Gtk->main_iteration while Gtk->events_pending; } sub parse_file { my $file = $_[0]; $file =~ s/\.gz$//; my $i = 0; gtkadd(my $win_pb = (gtkset_modal new Gtk::Window(), 1), gtkpack(new Gtk::VBox(5,0), " " . N("please wait, parsing file: %s", $files{ $_ }{desc}) . " ", my $pbar = new Gtk::ProgressBar() ) ); $win_pb->set_position('center'); $win_pb->realize(); $win_pb->show_all(); my $ey = $e_yes->get_chars(0, -1); my $en = $e_no->get_chars(0, -1); $ey =~ s/ OR /\|/; $ey =~ s/^\*$//; $en =~ s/^\*$/.*/; $ey = $ey .($::Word) if ($::isWord); if ($cal_mode) { my ($year, $month, $day) = $cal->get_date(); $ey = $months[$month]."\\s{1,2}$day\\s.*$ey.*\n"; } my @all = catMaybeCompressed ($file); if ($::isExplain) { my (@t, $t); while (@all) { $t = pop @all; next if ($t =~ /logdrake/); last if !($t =~ /$::Explain/); push @t, $t; } @all = reverse @t; } my $taille = @all; foreach (@all) { $i++; if ($i % 10) { $pbar->update($i/$taille); Gtk->main_iteration while Gtk->events_pending; } if (($en eq "") and /$ey/i) { logcolorize($_); next } if ((! /$en/i) and /$ey/i) { logcolorize($_); next } if ((! /$en/i) and ($ey eq "")) { logcolorize($_); next } } $win_pb->destroy(); if ($::isTail) { open F, $file or die "E: $!"; while () {}; #to prevent to output the file twice.. $log_text->set_point($log_text->get_length()); my $timer = Gtk->timeout_add(1000, \&input_callback); } } sub input_callback { logcolorize($_) while ; seek F, 0, 1; } ########################################################################################## sub logcolorize { # we get date & time if it is date & time (dmesg) s/(\D{3} .. (\d\d:\d\d:\d\d ))//; my $timestamp = $::isExplain ? $2 : $1; my @rec = split; log_output($cyan,$timestamp,$b); # date & time if any... $::isExplain or log_output(($rec[0] eq $h) ? $blue : $col,"$rec[0] ",$b); # hostname if ($rec[1] eq "last") { log_output($green," last message repeated ",$n); log_output($green, $rec[4], $b); log_output($green," times\n",$n); return; } # Extract PID if present if ($rec[1] =~ /\[(\d+)\]\:/) { my($pid) = $1; $rec[1] =~s/\[$1\]\:// ; log_output ($green, $rec[1] ."[",$n); log_output ($black, $pid,$b); log_output ($green, "]: ",$n); } else { log_output($green, $rec[1] ." ",$n); } for my $therest (2 .. $#rec) { $col = $cyan; # Check for keywords to highlight foreach (@word_good) { $col = $col_good if ($_ eq $rec[$therest]) } foreach (@word_warn) { $col = $col_warn if ($_ eq $rec[$therest]) } foreach (@word_bad) { $col = $col_bad if ($_ eq $rec[$therest]) } foreach (@word_note) { $col = $col_note if ($_ eq $rec[$therest]) } # Watch for words that indicate entire lines should be highlighted #foreach (@line_good) { $col = $col_good if ($_ eq $rec[$therest]) } #foreach (@line_warn) { $col = $col_warn if ($_ eq $rec[$therest]) } #foreach (@line_bad) { $col = $col_bad if ($_ eq $rec[$therest]) } log_output($col,"$rec[$therest] ",$n); } log_output($black,"\n",$n); } sub log_output { $log_text->insert($_[2],$_[0], undef,$_[1]); } #------------------------------------------------------------- # mail/sms alert #------------------------------------------------------------- sub alert_config { $::isWizard = 1; $::Wizard_pix_up = "wiz_logdrake.png"; # FIXME $::Wizard_title = N("Mail alert"); my $cron = q(#!/usr/bin/perl # generated by logdrake use MDK::Common; my $r = "*** ". chomp_(`date`) . " ***\n"; ); my $initdir = "/etc/init.d"; my ($load,$mail,$email,$smtp,); $load = 3; begin: $::Wizard_finished = 0; $::Wizard_no_previous = 1; $in->ask_okcancel(N("Mail alert configuration"), N("Welcome to the mail configuration utility.\n\nHere, you'll be able to set up the alert system.\n"), 1) or quit(); step_service: undef $::Wizard_no_previous; undef $::Wizard_finished; my $service = { httpd => N("Apache World Wide Web Server"), bind => N("Domain Name Resolver"), ftp => N("Ftp Server"), postfix => N("Postfix Mail Server"), samba => N("Samba Server"), sshd => N("SSH Server"), webmin => N("Webmin Service"), xinetd => N("Xinetd Service") }; my @installed_d = (); foreach my $serv (keys %$service) { -e "$initdir/$serv" && push (@installed_d,$serv); } $in->ask_from(N("service setting"), N("You will receive an alert if one of the selected services is no more running"), [ map { { label => "$_", val => \${ $_ }, type => "bool", text => "$service->{ $_ }" } } @installed_d ]) or goto begin; $cron .= "#- check services\n"; my $r; foreach (@installed_d) { if(!-e "/var/lock/subsys/$_") { $r .= "Service $_ ($service->{$_} is not running\n" }; # $cron .= "$r" if ${ $_ }; # take a look at this, don't know what is done here } step_load: undef $::Wizard_finished; $in->ask_from(N("load setting"), N("You will receive an alert if the load is higher than this value"), [ { label => "load ", val => \$load, type => 'range', min => 1, max => 50 }, ]) or goto step_service; $cron .= sprintf(<<'EOF', $load); #- load my ($load) = split ' ', first(cat_("/proc/loadavg")); $r .= "Load is huge: $load\n" if ($load > %s); EOF step_output: # $::Wizard_no_previous = 1; $::Wizard_finished = 1; $in->ask_from(N("alert configuration"), N("Please enter your email address below "), [ { label => "" }, { label => "Email", val => \$email}, ]) or goto step_load; $cron .= q(#- report it); $cron .= q( $email = ) . "'" . "$email" . "'" . ";\n\n"; $cron .= q( open F, '|/usr/sbin/sendmail -oi -t'; print F q(Subject: logdrake Mail Alert From: root@localhost To: ), "$email\n"; print F $r; # EOF); output "$cron_hourly", $cron; chmod 0755, $cron_hourly; print ("whole cron is ****** $cron *******\n"); undef $::isWizard; if (defined $::WizardWindow) { $::WizardWindow->destroy; undef $::WizardWindow; } } #------------------------------------------------------------- # menu callback functions #------------------------------------------------------------- sub save { $::isWizard=0; $yy = $in->ask_file(N("Save as.."),"/root") or return; output($yy,$log_text->get_chars(0,$log_text->get_length())); }