#!/usr/bin/perl -w ######################################################################## # # TITLE: # mgm, Maxwell's Generalized Monitor # # AUTHOR: # Scott Maxwell # # DESCRIPTION: # # Watch interrupts happen -- or other stuff. For kicks, try # # mgm -f 'date|' # mgm -f 'w|' # mgm -f 'free -k|' # mgm -f 'tail -10 /var/log/messages|' # mgm -f 'lpq|' # mgm -f 'fortune|' -d 5 # mgm -f 'procinfo|' # mgm -f 'ps auxw --sort:pcpu | head |' # # (Unfortunately, the last one doesn't work as I'd like because # the --sort:pcpu option to ps doesn't do what I think it's # supposed to.) # # TO DO: # # 1. Finish implementing the popup menu. # # 2. Currently there's no sanity checking on the size of the # window when auto-resizing. Maybe establish an upper limit and # add scroll bars if it's exceeded? # # 3. Read config file that looks something like # # # Personality File/process => Dlay Resize? Title # clock date| => 1 1 Clock # ctime uptimes| => 60 1 Internet Connect Time # snapshot procinfo| => 5 1 System Snapshot # ... # # CHANGE HISTORY # # $Log: mgm,v $ # Revision 1.3 1998/08/02 02:01:08 maxwell # Now automatically expands tabs, thanks to Text::Tabs. Maybe the tab # size should be settable. # # Revision 1.2 1998/08/02 01:57:15 maxwell # Added a pop-up menu, incompletely implemented it, then wandered away # for five months. # # Revision 1.1 1998/03/02 05:44:04 maxwell # Initial revision # ######################################################################## require 5.000; use strict; no strict qw(refs); $[ = 0; # Set array base to 0. $\ = "\n"; # Set output record separator. select(STDERR); $| = 1; # Flush output buffer (STDERR). select(STDOUT); $| = 1; # Flush output buffer (STDOUT). require '/home/maxwell/pm/MaxUtil.pm'; use Tk; use Tk::FileSelect; use Text::Tabs; ################ # # "Constants." # ################ my $TOP = new MainWindow(); my $TXT = $TOP->Text( width => 32, # W and H self-adjust if $AutoResize. height => 16, state => 'disabled', relief => 'flat', )->pack(fill => 'both', 'expand' => 1); ################ # # Globals. # ################ my $AutoResize = 1; # Flag: auto-resize window based on output? my $Delay = 1000; # Delay between updates. my $File = '/proc/interrupts'; # File to watch. my $Title = undef; # Window title (default is $File). ################ # # Do da work. # ################ while (scalar(@ARGV)) { my $opt = shift(@ARGV); if ($opt eq '-d') { $Delay = &SafeShift($opt) * 1000.0; } elsif ($opt eq '-f') { $File = &SafeShift($opt); } elsif ($opt eq '-h') { &Usage(); } elsif ($opt eq '-p') { &Usage('The -p option will specify a personality.'); } elsif ($opt eq '-r') { $AutoResize = 0; } elsif ($opt eq '-t') { $Title = &SafeShift(); } else { &Usage("Unknown option '$opt'."); } } $Title = $File unless (defined($Title)); $TOP->bind('' => \&PopupMenu); $TOP->title($Title); &Tick(); $TOP->repeat($Delay, \&Tick); &MainLoop(); exit(0); ################################################################ # # Timer callback. # ################################################################ sub Tick { open($File, $File) || die "Can't read $File: $!"; my @txt = expand(<$File>); close($File); my @config = (state => 'normal'); if ($AutoResize) { my ($w, $h) = (0, scalar(@txt)); foreach (@txt) { $w = length($_) if (length($_) > $w); } push(@config, width => $w, height => $h); } # Must re-enable and then disable, or we don't see anything (!). $TXT->configure(@config); $TXT->delete('1.0', 'end'); $TXT->insert('1.0', join('', @txt)); $TXT->configure(state => 'disabled'); } ################################################################ # # Print a usage message and exit with code 2. # ################################################################ sub Usage { print shift(@_) while (scalar(@_)); print <<"END_USAGE"; Usage: $0 [-d delay] [-f file] [-r] [-t title] [-h] -d: Delay between updates, in (optionally fractional) seconds. -f: Set file/process to watch. -p: Specify a personality as named in config file. Not done yet. -r: Turn off auto-resizing. -t: Set window title (default is filename). -h: Show this help message and exit with code 2. Note that the -f argument can be a plain filename, or it can end in a '|' character to specify a process to run -- e.g., mgm -f 'w|'. END_USAGE ; # Fool [c]perl-mode. exit(2); } ################################################################ # # Pop up window with given $title and get text from user, who then # clicks OK or Cancel. If OK, then update $$ref with entered text. # Not implemented yet. # ################################################################ sub GetText { my ($title, $ref) = @_; } ################################################################ # # Pop up the menu when the user clicks mouse-3. The BEGIN makes $m # act as a "static" variable (a la C). # ################################################################ BEGIN { my $m = undef; sub PopupMenu { $m->unpost() if (defined($m)); $m = $TOP->Menu(tearoff => 0); $m->add('command', label => sprintf("Toggle Auto-resize (currently %s)", $AutoResize ? 'On' : 'Off'), command => sub { $AutoResize = !$AutoResize; }); $m->add('command', label => "View a Different File (currently '$File')", command => sub { my ($dir, $file) = ('.', ''); if (defined($File) && ($File =~ m!^(.*)/(.*)$!)) { ($dir, $file) = ($1, $2); } $file = $TOP->FileSelect( directory => $dir, file => $file, )->Show(); $File = $file if (defined($file)); }); $m->add('command', label => "View a Different Process (currently '$File')", command => sub { &GetText('Process to Watch', \$File); $File = "$File|" unless ($File =~ /\|\s*$/); }); $m->add('command', label => "Change Delay (currently $Delay)", command => sub { &GetText('Delay (Fractional Seconds OK)', \$Delay); }); $m->add('command', label => "Change Title (currently '$Title')", command => sub { &GetText('Enter New Title', \$Title); }); $m->add('command', label => 'Quit', command => sub { $TOP->exit(); }); $m->add('separator'); $m->add('command', label => 'Oops! Just Close This Menu', command => sub { $m->unpost(); }); $m->post($TOP->winfo('pointerx'), $TOP->winfo('pointery')); } }