Category: Utilities
Author/Contact Info jepri@perlmonk.org
Description: Shows processes that are being started up or shut down (as they appear or disappear from your top display)
#!/usr/bin/perl
#By Jepri of PerlMonks
#
#Bottom complements top by showing you processes that are being
#created or destroyed as programs start up and shut down.
#
#It was originally written to help me figure out what a naughty
#third party vendor program was doing (forking and execing), but
#when I looked around there didn't seem to be anything like this.
#
#It will probably only work with Linux /proc, and could easily be 
#adapted to any other OS with the addition of parsers for other /procs
#
#Licensed under the GPL, which is longer than this program.  Go
#get your own copy

use strict;

if ( grep {/-h|-\?|help/} @ARGV)
    {
        print "bottom - Display processes as they are created or destr
+oyed";
        print "\n\nShows you processes as they are started and stopped
+.\n";
        print "Note that there is a minimum time that we can measure -
+ \n";
        print "if a process starts and stops in under about 10ms we wo
+n't\n";
        print "see it at all.\n\n";
        print "Options are:\n\n";
        print "-ts or --timestamp   Print the time\n";
        print "-u  or --user        Print who owns(owned) the process\
+n";
        print "-v  or --verbose     Print bells AND whistles\n";
        print "\n\n\n";
        exit;
    }
my %ps;
my %old;
my $timestamp=(grep {/--timestamp|-ts/} @ARGV) ? 1 : 0 ;
my $displayuser=(grep {/--user|-u/} @ARGV) ? 1 : 0 ;
if ( grep {/-v|--verbose/} @ARGV) 
    {
        $timestamp=1;
        $displayuser=1;
    }

while (1)
    {
        opendir DH, "/proc";
        my @procs = readdir(DH);
        close DH;
        my %new;
        foreach ( @procs )
            {
                $new{$_} = 1 if ($_ > 0);
            }
        
        my ($h1, $h2) = cmp_hsh( \%new, \%old);
        %old = %new;
    }

{
my %registry;
my %num_of;

sub regdump
    {
        return join(",", keys %registry);
    }
#Add a key-value pair to the registry
sub reg 
    {
        my ( $key, $val ) = @_;
        $registry{$key} = $val;
    }
#Given a key, return the value stored
sub what
    {
        my $key = shift;
        return $registry{$key};
    }
#Given a key, delete it and its value
sub unreg
    {
        my $key = shift;
        delete $registry{$key};
    }
}

sub get_proc_name
    {
        my $id = shift;
        open FH, "/proc/$id/status";
        my $name = <FH>;
        chomp $name;
        $name =~ s/Name:\s+//;
        if ( $displayuser )
            {
                my $user;
                grep { (/Uid:\s+(\d+)/) && ($user=getpwuid($1)) } <FH>
+;
                $name.="\t(".$user.")";
            }
        close FH;
        return $name;
    }
#Return a timestamp
sub time_stamp
    {
        if ( $timestamp )
            {
                my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday
+, $isdst) = localtime(time);
                my $ts = $mday.'/'.$mon.'/'.(1900+$year).':'.$hour.':'
+.$min.':'.$sec;
                return $ts."\t";
            }
        return "";
    }

#Figure out which keys have been added and which have been
#deleted, print them out with appropriate markings.
sub cmp_hsh
    {
        my ( $h1, $h2 ) = @_;
        my ( %oh1, %oh2);
        foreach ( keys %{ $h1 } )
            {
                unless ( $h2->{$_} )
                    {
                        $oh1{$_}=get_proc_name($_);
                        reg($_, $oh1{$_});
                        print time_stamp(), "^^^ ", what($_), "\n";
                    }
            }
        foreach ( keys %{ $h2 } )
            {
                unless ( $h1->{$_} )
                    {
                        print time_stamp(), "vvv ", what($_), "\n";
                        $oh2{$_}=what($_);
                        unreg($_);
                    }
            }
        return \%oh1, \%oh2;
    }