#!/usr/local/bin/perl
#
# @(#)DiskUsage.pl	1.11 [07/23/99 - 13:54:03]
#
# author: ron dilley
#
# desc: this perl script gathers disk usage info
#
############################################################################

#
# defines
#
$TRUE = 1;
$FALSE = 0;
$FAILED = -1;

$VERSION = "1.11";

$debug = $FALSE;
$verbose = $FALSE;
$track = $FALSE;
$filesize = 1000;
$fileage = 1;

$starting_path = ".";

#
# main routine
#
if ( &main() != $TRUE ) {
  exit( 1 );
}

exit( 0 );

############################################################################
#
# sub-routines
#

#
# main routine
#
sub main {
  &show_banner();

  if ( &parse_command_line() != $TRUE ) {
    return FAILED;
  }

  if ( &scan_dirs() != $TRUE ) {
    return $FAILED;
  }

  if ( $track != $TRUE ) {
    &show_utilization();
  }

  return TRUE;
}

#
# display banner info
#
sub show_banner {
  print "Disk Utilization Tool v$VERSION\n";
  print "By: Ron Dilley\n";
  print "\n";

  return $TRUE;
}

#
# display help info
#
sub show_help {
  print "Syntax:\n";
  print "\n";
  print "DiskUsage.pl [-dir {path}] [options]\n";
  print "\n";
  print "-dir\tSearch a directory other than the default \'.\'\n";
  print "-mail {size}\tSend mail to user if consuming more space than argument\n";
  print "-verbose\tDisplay status information during program run\n";
  print "-debug\tDisplay debug information during program run\n";
  print "-track\tDisplay disk usage in a verbose format to help\n";
  print "\t\ttrack down disk consumers.\n";
  print "\t\t-size\tOverride default size value when using the 'track' feature\n";
  print "\t\t-age\tOverride default age value when using the 'track' feature\n";
  print "\n";

  return $TRUE;
}

#
# parse command-line arguments
#
sub parse_command_line {
  local ( @args = @ARGV );

  print "CONFIG - Parsing command line . . .";

  while( $_ = shift( @args ) ) {
    if ( /^-/ ) {
      if ( /^-dir$/ ) {
	while( substr( $args[0], 0, 1 ) ne '-' && @args > 0 ) {
	  $starting_path = shift( @args );
	}
      } elsif ( /^-track$/ ) {
	$track = $TRUE;
      } elsif ( /^-size$/ ) {
	$track = $TRUE;
	while( substr( $args[0], 0, 1 ) ne '-' && @args > 0 ) {
	  $filesize = shift( @args );
	}
      } elsif ( /^-age$/ ) {
	$track = $TRUE;
	while( substr( $args[0], 0, 1 ) ne '-' && @args > 0 ) {
	  $fileage = shift( @args );
	}
      } elsif ( /^-verbose$/ ) {
	$verbose = $TRUE;
      } elsif ( /^-debug$/ ) {
	$debug = $TRUE;
      } elsif ( /^(-help|-h|-?)$/ ) {
	&show_help();
	return $FAILED;
      } else {
	print "WARNING - Unknown argument \[$_\]\n";
      }
    }
  }

  print " Done!\n";

  return $TRUE;
}

#
# collect directory info
#
sub scan_dirs {
  print "STATUS - Target path is \[$starting_path\]\n";
    
  print "\n";

  system( "/bin/cd $starting_path" );
  open( DF, "/usr/sbin/df -k $starting_path |" );

  while( $line = <DF> ) {
    chop( $line );

    print "$line\n";
  }

  close( DF );

  print "\n";

  if ( $track ) {
    print "STATUS - Searching for files larger than ${filesize}K, modified in the last $fileage day(s)\n\n";
  } else {
    print "STATUS - Scanning in progress\n";
  }

  &scan_dir( "$starting_path" );

  return $TRUE;
}

#
# scan dir (re-entrant)
#
sub scan_dir
{
  local( $current_dir ) = @_;
  local( @entries, $entry );
	
  if ( $verbose ) {
    print "STATUS - Scanning \[$current_dir\]\n";
  }

  push( @dir_stack, 0 );

  opendir( $dir_stack[0], $current_dir );
  @entries = grep( !/^\.\.?$/, readdir( $dir_stack[0] ) );

  foreach $entry ( @entries ) {
    if (! -l "$current_dir/$entry" ) {
      if ( -d "$current_dir/$entry" && $entry ne ".snapshot" ) {
	if ( $debug ) {
	  print "DEBUG - DIR\t$entry\n";
	}
	&scan_dir( "$current_dir/$entry" );
      } elsif( -f "$current_dir/$entry" ) {
	&collect_data( $entry, $current_dir, ( stat( "$current_dir/$entry" ) )[4,5,7,8,9,10] );
      }
    } else {
      if ( $debug ) {
	print "DEBUG - \[$entry\] is not a file or directory\n";
      }
    }
  }

  closedir( $dir_stack[0] );

  pop( @dir_stack );

  return $TRUE;
}

#
# collect and store file data
#
sub collect_data
{
  local( $file, $path, $uid, $gid, $size ) = @_;

  if ( $size <= 1024 ) {
    $size_in_k = 1;
  } else {
    $size_in_k = int ( $size / 1024 );
  }

  $days_old = int ( -M "$path/$file" );

  if ( $debug ) {
    print "DEBUG - $file\t$path\t$uid\t$gid\t${size_in_k}K\t${days_old} days old\n";
  }

  if ( $days_old >= 0 ) {
    if ( $track ) {
      if ( $size_in_k >= $filesize && $days_old <= $fileage ) {
	printf( "%10sK %s/%s\n", $size_in_k, $path, $file );
      }
    } else {
      $util_by_age[$days_old] += $size_in_k;
      $util_by_uid{$uid} += $size_in_k;
      $util_by_gid{$gid} += $size_in_k;
    }
  } else {
    print "ERROR - Bad file date on [$path/$file\]\n";
  }

  return $TRUE
}

#
# display disk utilization
#
sub show_utilization {
  local( $uid, $gid );

  print "\n";
  print "Disk Utilization Statistics:\n";
  print "============================\n";
  print "\n";

  show_util_by_uid();
  show_util_by_gid();
  show_util_by_age();

  return $TRUE;
}

#
# Utilization by uid
#
sub show_util_by_uid {
  local( $uid, $gid );

  print "UID             Space used\n";
  print "--------------------------\n";

  foreach $uid ( sort {$util_by_uid{$b} <=> $util_by_uid{$a} } keys( %util_by_uid ) ) {
    ( $name ) = ( getpwuid( $uid ) )[0];
    
    if ( $name ne "" ) {
      $col1 = $name;
    } else {
      $col1 = $uid;
    }

    $total_by_uid += $util_by_uid{$uid};
				
    if ( $util_by_uid{$uid} < 1000 ) {
      $col2 = $util_by_uid{$uid} . "K";
    } elsif ( $util_by_uid{$uid} < 1000000 ) {
      $col2 = int( $util_by_uid{$uid} / 1000 ) . "M";
    } else {
      $col2 = int( $util_by_uid{$uid} / 1000000 ) . "G";
    }

    printf( "%-15s %10s", $col1, $col2 );

    if ( $verbose ) {
      printf( "\t\$%10.00f", ( $util_by_uid{$uid} / 1000000 ) * 2500 );
    }

    print "\n";
  }
 	
  print "                 ---------\n";

  if ( $total_by_uid < 1000 ) {
    printf( "%25sK\n\n", $total_by_uid );	
  } elsif ( $total_by_uid < 1000000 ) {
    printf( "%25sM\n\n", int( $total_by_uid / 1000 ) );
  } else {
    printf( "%25sG\n\n", int( $total_by_uid / 1000000 ) );
  }

  return $TRUE;
}

#
# Utilization by gid
#
sub show_util_by_gid {
  print "GID             Space used\n";
  print "--------------------------\n";

  foreach $gid ( sort {$util_by_gid{$b} <=> $util_by_gid{$a} } keys( %util_by_gid ) ) {
    ( $name ) = ( getgrgid( $gid ) )[0];

    if ( $name ne "" ) {
      $col1 = $name;
    } else {
      $col1 = $gid;
    }

    $total_by_gid += $util_by_gid{$gid};
		
    if ( $util_by_gid{$gid} < 1000 ) {
      $col2 = $util_by_gid{$gid} . "K";
    } elsif ( $util_by_gid{$gid} < 1000000 ) {
      $col2 = int( $util_by_gid{$gid} / 1000 ) . "M";
    } else {
      $col2 = int( $util_by_gid{$gid} / 1000000 ) . "G";
    }

    printf( "%-15s %10s", $col1, $col2 );
	
    if ( $verbose ) {
      printf( "\t\$%10.00f", ( $util_by_gid{$gid} / 1000000 ) * 2500 );
    }

    print "\n";
  }

  print "                 ---------\n";

  if ( $total_by_gid < 1000 ) {
    printf( "%25sK\n\n", $total_by_gid );	
  } elsif ( $total_by_gid < 1000000 ) {
    printf( "%25sM\n\n", int( $total_by_gid / 1000 ) );
  } else {
    printf( "%25sG\n\n", int( $total_by_gid / 1000000 ) );
  }

  return $TRUE;
}

#
# Show utilization by age
#
sub show_util_by_age {
  local( $total, $total_by_age, $col2, $min, $max, $i );

  print "Age in days     Space used\n";
  print "--------------------------\n";

  push( @ranges, ( 0, 5 ) );
  push( @ranges, ( 6, 10 ) );
  push( @ranges, ( 11, 30 ) );
  push( @ranges, ( 31, 60 ) );
  push( @ranges, ( 61, 90 ) );
  push( @ranges, ( 91, 182 ) );
  push( @ranges, ( 183, 365 ) );
  push( @ranges, ( 366, 99999 ) );

  while( @ranges > 0 ) {
    $max = pop( @ranges );
    $min = pop( @ranges );
    $total = 0;

    for ( $i = $min; $i <= $max && $util_by_age[$i] != $FAILED; $i++ ) {			
      $total += $util_by_age[$i];
    }
	
    $total_by_age += $total;

    if ( $total < 1000 ) {
      $col2 = $total . "K";
    } elsif ( $total < 1000000 ) {
      $col2 = int( $total / 1000 ) . "M";
    } else {
      $col2 = int( $total / 1000000 ) . "G";
    }

    printf( "%4s - %-8s %10s\n", $min, $max, $col2 );
  }
    
  print "                 ---------\n";
	
  if ( $total_by_age < 1000 ) {
    printf( "%25sK\n\n", $total_by_age );	
  } elsif ( $total_by_age < 1000000 ) {
    printf( "%25sM\n\n", int( $total_by_age / 1000 ) );
  } else {
    printf( "%25sG\n\n", int( $total_by_age / 1000000 ) );
  }

  return $TRUE;
}

#
# send e-mail alert to disk hog
#
sub send_mail_notice {
  my ( $username, $total ) = @_;

  return $TRUE;
}

