1: #!/usr/bin/perl -w
   2: 
   3: require 5;
   4: use strict;
   5: use Getopt::Std;
   6: use File::Find;
   7: use vars qw(%opts @mailboxen $file $access $mod $user $unread $i $j @passwd $name @oldusers $mailbytes $mailsize $homebytes $homesize @sorted @units @multipliers $limit $outputstring $length);
   8: 
   9: getopts('ho:al:', \%opts);
  10: 
  11: sub usage(){
  12:     print <<EOF;
  13: Usage: $0 [options]
  14: Example: $0 -om -l m=10m
  15: Synopsis: Checks the age and size of all mailboxen on the system, to help you isolate dead accounts, or disk hogs.
  16: 
  17: -h : Displays this help screen.
  18: -o : Order of results[1].
  19: -a : List all users, not just those with unread mail.
  20: -l : Limit. Format is 'F=LLU', comma seperated, where 'F' is the field[2], 'L' the limit, and 'U' the unit[3].
  21:      Example: $0 -l u=1w,m=5m
  22: (This lists all users with unread mail who haven't checked there mailbox in over a week, and which is over 5 megabytes in size)
  23: 
  24: [1]: Can be ordered by 'a' (alphabetic), 'u' (time mailbox unread), 'm' (mailbox size), 'h' (home dir size) or 't' (total disk usage).
  25: [2]: Field can be any listed above, except 'a'.
  26: [3]: Unit is the first letter of whichever unit you wish to use.
  27:      i.e. for times: 's' (second), 'm' (minute), 'h' (hour), 'd' (day), 'w' (week) or 'y' (year).
  28:      or for memory: 'b' (bytes), 'k' (Kb), 'm' (Mb) or 'g' (Gb).
  29: 
  30: EOF
  31:     exit;
  32: }
  33: 
  34: if ($opts{'h'}){
  35:     usage();
  36: }
  37: 
  38: sub subdivide ($$\@\@$) {
  39: 
  40:     my ($ordinal, $unit, $result);
  41:     my $time = shift;
  42:     my $depth = shift;
  43:     my $mult_ref = shift;
  44:     my @multipliers = @$mult_ref;
  45:     my $unit_ref = shift;
  46:     my @Units = @$unit_ref;
  47:     my $pluralise = shift;
  48: 
  49:     my $k;
  50:     my $l = 1;
  51:     for ($k=0; $k<@units; $k++) {
  52: 
  53:         if ($k == @multipliers || $time < $l * $multipliers[$k]) {
  54:             $ordinal = int ( $time / $l );
  55:             $unit = $units[$k];
  56:             $time -= $ordinal * $l;
  57:             last;
  58:         }
  59: 
  60:         $l *= $multipliers[$k];
  61:     }
  62: 
  63:     if ($pluralise && ($ordinal > 1)) {
  64:         $unit .= 's';
  65:     }
  66: 
  67:     $result = "$ordinal $unit";
  68: 
  69:     if ($depth && $time) {
  70:         $result .= ", " . subdivide($time, --$depth, \@multipliers, \@units, $pluralise);
  71:     }
  72: 
  73:     return $result;
  74: }
  75: 
  76: sub multiplyup ($\@\@) {
  77: 
  78:     my $time = shift;
  79:     my $result = substr ($time, 0, -1);
  80:     my $unit = substr ($time, -1, 1);
  81:     my $mult_ref = shift;
  82:     my @multipliers = @$mult_ref;
  83:     my $unit_ref = shift;
  84:     my @Units = @$unit_ref;
  85: 
  86:     my $k;
  87:     for ($k=0; $k<@units; $k++) {
  88: 
  89:         if ($unit eq $units[$k]) {
  90:             last;
  91:         }
  92: 
  93:         $result *= $multipliers[$k];
  94:     }
  95: 
  96:     return $result;
  97: }
  98: 
  99: open PASSWD, "/etc/passwd" or die "Could now open /etc/passwd, wtf?";
 100: @passwd = <PASSWD>;
 101: 
 102: opendir MAIL, "/var/spool/mail" or die "Could not open /var/spool/mail, wtf?";
 103: @mailboxen = grep { ! /^\..*$/  } readdir MAIL;
 104: closedir MAIL;
 105: 
 106: my $longestname = 0;
 107: 
 108: for ($i=0; $i<@mailboxen; $i++) {
 109: 
 110:     $user = $mailboxen[$i];
 111:     $file = "/var/spool/mail/$user";
 112: 
 113:     $access = (-A $file);
 114:     $mod = (-M $file);
 115: 
 116:     if ($access > $mod || $opts{'a'}) {
 117: 
 118:         @multipliers = (60, 60, 24, 7, 365/7);
 119:         @units = ('second', 'minute', 'hour', 'day', 'week', 'year');
 120:         $unread = $access > $mod ? subdivide(int($access * 86400), 1, @multipliers, @units, 1) : "no time";
 121: 
 122:         for ($j=0; $j<@passwd; $j++) {
 123:             if ($passwd[$j] =~ /^$user:[^:]+:[^:]+:[^:]+:([^,:]+).+$/) {
 124:                 $name = $1;
 125:                 $name =~ s/\\//;
 126:                 $name =~ s/(\b)(\w+)/$1\u\L$2/g;
 127:                 last;
 128:             }
 129:         }
 130: 
 131:         if (length($name) > $longestname) { $longestname = length($name) }
 132: 
 133:         @multipliers = (1024, 1024, 1024);
 134:         @units = ('bytes', 'Kb', 'Mb', 'Gb');
 135:         $mailbytes = -s $file;
 136:         $mailsize = subdivide($mailbytes, 0, @multipliers, @units, 0);
 137: 
 138:         $homebytes = 0;
 139:         find sub { $homebytes += -s }, "/home/$user";
 140:         $homesize = subdivide($homebytes, 0, @multipliers, @units, 0);
 141: 
 142:         push @oldusers, { user      => "$user",
 143:                           unread    => "$unread",
 144:                           name      => "$name",
 145:                           access    => $access > $mod ? "$access" : 0,
 146:                           mailbytes => "$mailbytes",
 147:                           mailsize  => "$mailsize",
 148:                           homebytes => "$homebytes",
 149:                           homesize  => "$homesize" };
 150: 
 151:     }
 152: }
 153: 
 154: if ( $opts{'l'} ) {
 155:     my %limits = split /[,=]/, $opts{'l'};
 156: 
 157:     if ( $limits{'u'} ) {
 158:         @multipliers = (60, 60, 24, 7, 365/7);
 159:         @units = ('s', 'm', 'h', 'd', 'w', 'y');
 160:         $limit = multiplyup($limits{'u'}, @multipliers, @units) / 86400;
 161:         @oldusers = grep { $_->{access} > $limit } @oldusers;
 162:     }
 163: 
 164:     if ( $limits{'m'} ) {
 165:         @multipliers = (1024, 1024, 1024);
 166:         @units = ('b', 'k', 'm', 'g');
 167:         $limit = multiplyup($limits{'m'}, @multipliers, @units);
 168:         @oldusers = grep { $_->{mailbytes} > $limit } @oldusers;
 169:     }
 170: 
 171:     if ( $limits{'h'} ) {
 172:         @multipliers = (1024, 1024, 1024);
 173:         @units = ('b', 'k', 'm', 'g');
 174:         $limit = multiplyup($limits{'h'}, @multipliers, @units);
 175:         @oldusers = grep { $_->{homebytes} > $limit } @oldusers;
 176:     }
 177: 
 178:     if ( $limits{'t'} ) {
 179:         @multipliers = (1024, 1024, 1024);
 180:         @units = ('b', 'k', 'm', 'g');
 181:         $limit = multiplyup($limits{'t'}, @multipliers, @units);
 182:         @oldusers = grep { $_->{mailbytes} + $_->{homebytes} > $limit } @oldusers;
 183:     }
 184: 
 185: }
 186: 
 187: 
 188: if ( !defined( $opts{'o'} ) || $opts{'o'} eq 'u') {
 189:     @sorted = sort { $b->{access} <=> $a->{access} } @oldusers;
 190: }
 191: elsif ($opts{'o'} eq 'm') {
 192:     @sorted = sort { $b->{mailbytes} <=> $a->{mailbytes} } @oldusers;
 193: }
 194: elsif ($opts{'o'} eq 'h') {
 195:     @sorted = sort { $b->{homebytes} <=> $a->{homebytes} } @oldusers;
 196: }
 197: elsif ($opts{'o'} eq 't') {
 198:     @sorted = sort { $b->{mailbytes} + $b->{homebytes} <=> $a->{mailbytes} + $a->{homebytes} } @oldusers;
 199: }
 200: elsif ($opts{'o'} eq 'a') {
 201:     @sorted = sort { $a->{name} cmp $b->{name} } @oldusers;
 202: }
 203: else {
 204:     warn "Bad ordering option, returning results unordered.";
 205: }
 206: 
 207: print "Name" . ' ' x ($longestname+1) ."Unread For              Mailbox Size   Home Dir Size\n";
 208: 
 209: for ($i=0; $i<@sorted; $i++) {
 210:     $outputstring = "$sorted[$i]{name} " . '-' x $longestname;
 211:     $outputstring = substr($outputstring, 0, $longestname + 4);
 212: 
 213:     $outputstring .= " $sorted[$i]{unread} ----------------------";
 214:     $outputstring = substr($outputstring, 0, $longestname + 40 - length($sorted[$i]{mailsize}));
 215: 
 216:     $outputstring .= " $sorted[$i]{mailsize} ------------";
 217:     $outputstring = substr($outputstring, 0, $longestname + 56 - length($sorted[$i]{homesize}));
 218: 
 219:     $outputstring .= " $sorted[$i]{homesize}\n";
 220: 
 221:     print $outputstring;
 222: }
 223: 
 224: exit;