durep-0.9/0002755000175000017500000000000010116703564013330 5ustar psirenpsiren00000000000000durep-0.9/Artistic0000644000175000017500000001373707157410300015040 0ustar psirenpsiren00000000000000 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End durep-0.9/BUGS0000644000175000017500000000011610116702351014000 0ustar psirenpsiren00000000000000None that I know of. If you find one, let me know, or send me a patch please! durep-0.9/CHANGES0000644000175000017500000000440010116704453014315 0ustar psirenpsiren00000000000000Changes since version 0.8 ------------------------- * Complete rewrite which now uses a cgi script to generate web reports. * Removed pointless --accessed, --modified, --users and --xusers options amonsgt others, to simplify code (this sort of task is better done with find). * HTML should be a lot cleaner, and now uses stylesheets. * Save and load files now use MLDBM module, which makes them a lot more compact and faster to search. Changes since version 0.7 ------------------------- * Added --save-file and --load-file for persistant data. This will be used more in the future for extra functionality. * Added --expand-path option. (Joern Clausen). * Made sure that the chdir returns us to the original working directory. This was causing some problems when specifying the web directory which had to be an absolute path. (Joern Clausen). * Removed the ID entry from the hash. It was only used in the make_web_pages subroutine which does it on the fly now. (This was needed to work with save files). * Added headings to the web page output. (Paul M. Lambert). * Removed the round subroutine and replaced it with "int($x+0.5)". (Paul M. Lambert). * Several minor changs and code cleanups. Also rearranged some internals to make it easiear to add features later. * Made a proper man page. This is created from the durep.pod file by pod2man. * Added a (small) makefile. This creates the man page and installs the script. Changes since version 0.6 ------------------------- * Removed --include and --exclude options. Replaced with --exclude-path which has far more control. * Also added --collapse-path for hiding contents of directories while still including their sizes in calculations. Collapsed paths are shown in green. (Thanks to Joern Clausen for the idea) * Removed --depth and replaced with --text-depth and --web-depth to allow for more specific control. (Thanks to Joern Clausen for the idea) * Added minor patch from Jason Allen to fix IE rendering of bar graphs. Good ol' Microsoft. * Now includes full path at root node rather than abreviations such as .. or . * Empty directories or directories that have no entries to match the options are now dealt with better. * Several minor cosmetic enhancements and internal changes. durep-0.9/Makefile0000644000175000017500000000105710116702351014762 0ustar psirenpsiren00000000000000## Edit these locations as necessary PREFIX = /usr/local CGI_PREFIX = /usr/lib/cgi-bin CSS_PREFIX = /var/www/durep PNG_PREFIX = /var/www/durep VERSION = 0.9 # Make documentation doc: pod2man --center="Disk Usage Report Generator" --release="durep version $(VERSION)" durep.pod > durep.1 # Make install install: doc install -m 755 durep $(PREFIX)/bin install -m 644 durep.1 $(PREFIX)/man/man1 install -m 644 durep.cgi $(CGI_PREFIX) install -m 644 style.css $(CSS_PREFIX) install -m 644 bar.png $(PNG_PREFIX) # Clean all the crap up clean: @rm -f *~durep-0.9/README0000644000175000017500000000137410116702351014204 0ustar psirenpsiren00000000000000------------------------------------------------------------------------- You may distribute this program under the terms of the Artistic License. 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 Artistic License for more details. ------------------------------------------------------------------------- See the man page for documentation (generated on make install from durep.pod). INSTALLATION INSTRUCTIONS ------------------------- 1. Edit Makefile and change PREFIXes if necessary. 2. make install Damian Kramer (psiren@hibernaculum.net) durep-0.9/THANKS0000644000175000017500000000057607157410300014243 0ustar psirenpsiren00000000000000Thanks to: Roger Dunce Alpha testing and patches for my blind spots with divison by zero ;) Joern Clausen Several ideas for collapsing paths and web-depth options. Jason Allen Minor fix for IE rendering of bar graphs. Paul M. Lambert Patches for rounding and web page headings.durep-0.9/bar.png0000644000175000017500000000031510116674427014604 0ustar psirenpsiren00000000000000‰PNG  IHDR%¥ùbKGD#[®.¥¡ pHYs  šœtIMEÔ  Ç£6<tEXtCommentCreated with The GIMPïd%n1IDATxÚEÉÁ A††¬ÏŽí!|i~{sXû„±@X`Z`RO3,Ð=Y`Ü_Â3)H· …+IEND®B`‚durep-0.9/durep0000755000175000017500000003015710116674427014406 0ustar psirenpsiren00000000000000#!/usr/bin/perl -w ############################################################################# # durep - Disk Usage Report Generator # # # # Copyright (C) 2004 Damian Kramer (psiren@hibernaculum.net) # # # # You may distribute this program under the terms of the Artistic License. # # # # 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 # # Artistic License for more details. # ############################################################################# use Getopt::Long; use File::Basename; use MLDBM qw(DB_File Storable); use Fcntl; use Sys::Hostname; use POSIX; use Cwd qw(cwd); use strict; our ($version, %options); our ($root_node, $filesystem_id, @stats); our ($opt_help, $opt_version, $opt_textdepth, $opt_hidesize, $opt_showdate, $opt_nosort, $opt_quiet); our ($opt_savefile, $opt_loadfile, $opt_desc, $opt_collate); our ($opt_files, $opt_onefilesystem, $opt_collapsepath, $opt_excludepath, $opt_coalescefiles); our ($root_dir, $file_count, $dir_count, $next_id); our ($TYPE_FILE, $TYPE_DIR, $TYPE_EMPTY, $TYPE_COALESCED, $TYPE_COLLAPSED); $TYPE_FILE = 0; $TYPE_DIR = 1; $TYPE_EMPTY = 2; $TYPE_COALESCED = 4; $TYPE_COLLAPSED = 8; $version = "0.9"; $| = 1; $SIG{INT} = \&catchError; %options = ("h|help" => \$opt_help, "v|version" => \$opt_version, "td|text-depth=i" => \$opt_textdepth, "hs|hide-size=s" => \$opt_hidesize, "sd|show-date" => \$opt_showdate, "ns|nosort" => \$opt_nosort, "q|quiet" => \$opt_quiet, "sf|save-file=s" => \$opt_savefile, "lf|load-file=s" => \$opt_loadfile, "d|desc=s" => \$opt_desc, "c|collate=s" => \$opt_collate, "f|files" => \$opt_files, "x|one-file-system" => \$opt_onefilesystem, "cp|collapse-path=s" => \$opt_collapsepath, "ep|exclude-path=s" => \$opt_excludepath, "cf|coalesce-files=s" => \$opt_coalescefiles ); &usage unless (GetOptions(%options)); &usage if(defined $opt_help); if(defined $opt_version) { print "durep version $version\n"; exit 0; } ## Process options if($opt_collate) { collate(); exit 0; } $opt_hidesize = processSizeOption($opt_hidesize) if(defined $opt_hidesize); $opt_coalescefiles = processSizeOption($opt_coalescefiles) if(defined $opt_coalescefiles); doAbort("Depth must be greater than 0.") if(defined $opt_textdepth && $opt_textdepth < 1); doAbort("You must specify a save file if you use --quiet.") if(defined $opt_quiet && !defined $opt_savefile); doAbort("You can't use --load-file and --save-file at the same time.") if(defined $opt_loadfile && defined $opt_savefile); if($opt_savefile) { $opt_savefile .= ".ds" unless $opt_savefile =~ /\.ds$/; } push @ARGV, "." unless @ARGV; $root_dir = shift; chop $root_dir if $root_dir =~ m|./$|; # Remove trailing / doAbort("`$root_dir' not a valid directory.") unless -d $root_dir; # Get the absolute pathname rather than relative pathname $_ = cwd(); chdir($root_dir) or doAbort("Unable to chdir to '$root_dir'."); $root_dir = cwd(); chdir($_); if($opt_loadfile) { tie %{$root_node}, 'MLDBM', "$opt_loadfile", O_RDONLY, 0640 or doAbort("Unable to tie file '$opt_loadfile'."); } else { ## Perform scan $file_count = 0; $dir_count = 0; $next_id = 1; $root_node = {}; if($opt_savefile) { if(-r $opt_savefile) { print "Removing existing savefile '$opt_savefile'.\n" unless $opt_quiet; unlink $opt_savefile; } tie %{$root_node}, 'MLDBM', "$opt_savefile", O_CREAT|O_RDWR, 0640 or doAbort("Unable to tie file '$opt_savefile'."); } ($filesystem_id) = stat $root_dir if defined $opt_onefilesystem; $root_node->{1} = recursiveScan($root_dir, undef, 1); my $data; $data->{FILE_COUNT} = $file_count; $data->{DIR_COUNT} = $dir_count; $data->{LAST_UPDATE} = time; $data->{HOSTNAME} = hostname(); $data->{DESC} = $opt_desc if $opt_desc; $data->{OPTIONS}->{FILES} = 1 if $opt_files; $data->{OPTIONS}->{ONEFILESYSTEM} = 1 if $opt_onefilesystem; $data->{OPTIONS}->{COLLAPSEPATH} = $opt_collapsepath if $opt_collapsepath; $data->{OPTIONS}->{EXCLUDEPATH} = $opt_excludepath if $opt_excludepath; $data->{OPTIONS}->{COALESCEFILES} = $opt_coalescefiles if $opt_coalescefiles; $root_node->{DATA} = $data; } # use Data::Dumper; # print Dumper($root_node); if(!$opt_quiet) { printf "[ %s %s (%d files, %d dirs) ]\n", $root_node->{1}->{NAME}, prettyFileSize($root_node->{1}->{SIZE}), $root_node->{1}->{FCOUNT}, $root_node->{1}->{DCOUNT}; printDir($root_node->{1}, 0); } if($opt_savefile || $opt_loadfile) { untie %{$root_node}; } exit 0; ## End of program. sub recursiveScan { my ($dir, $parent, $store) = @_; my @children; my $coalesced_count = 0; my $coalesced_size = 0; my $node = {}; my $temp; $node->{ID} = $next_id++; if(defined $parent) { $node->{NAME} = basename($dir); $node->{PARENT} = $parent; } else { $node->{NAME} = $dir; } $node->{SIZE} = 0; $node->{TYPE} = $TYPE_DIR; $node->{DCOUNT} = 0; $node->{FCOUNT} = 0; if($store) { $store = 0 if($opt_collapsepath && $dir =~ m/$opt_collapsepath/); } $node->{TYPE} &= $TYPE_COLLAPSED unless $store; opendir(DIR, $dir) or warn "Unable to open dir '$dir': $!\n" and return $node; foreach(readdir(DIR)) { @stats = lstat "$dir/$_" or warn "Unable to lstat '$dir/$_': $!\n" and next; $node->{MTIME} = $stats[9] if($_ eq "."); # Skip '.' and '..' next if(/^\.{1,2}$/); if(-d _ && ! -l _ && !$opt_files) { if(! -x _) { warn "Unable to read directory `$dir/$_'. Skipping.\n"; next; } next if($opt_excludepath && "$dir/$_" =~ m/$opt_excludepath/); next if($opt_onefilesystem && ($stats[0] != $filesystem_id)); $temp = recursiveScan("$dir/$_", $node->{ID}, $store); $node->{SIZE} += $temp->{SIZE}; if($store) { $root_node->{$temp->{ID}} = $temp; push @children, $temp->{ID}; $dir_count++; $node->{DCOUNT}++; } next; } if($opt_coalescefiles && $stats[7] < $opt_coalescefiles) { $coalesced_count++; $coalesced_size += $stats[7]; } else { if($store) { my $file = {}; $file->{ID} = $next_id++; $file->{NAME} = $_; $file->{SIZE} = $stats[7]; $file->{PARENT} = $node->{ID}; $file->{TYPE} = $TYPE_FILE; $file->{MTIME} = $stats[9]; $root_node->{$file->{ID}} = $file; push @children, $file->{ID}; } $node->{SIZE} += $stats[7]; } $file_count++; $node->{FCOUNT}++; } closedir(DIR); if($coalesced_count) { if($store) { my $file = {}; $file->{ID} = $next_id++; $file->{NAME} = "[COALESCED FILES]"; $file->{SIZE} = $coalesced_size; $file->{PARENT} = $node->{ID}; $file->{TYPE} = $TYPE_FILE|$TYPE_COALESCED; $file->{MTIME} = 0; $file->{FCOUNT} = $coalesced_count; $root_node->{$file->{ID}} = $file; push @children, $file->{ID}; } $node->{SIZE} += $coalesced_size; } if(@children) { $node->{CHILDREN} = \@children; } else { $node->{TYPE} = $TYPE_DIR|$TYPE_EMPTY; } # $root_node->{$node->{ID}} = $node; return $node; } sub collate { my %db; doAbort("'$opt_collate' is not a valid directory.") unless -d $opt_collate; tie %db, 'MLDBM', "$opt_collate/durep.cds", O_CREAT|O_RDWR, 0640 or doAbort("Unable to tie file '$opt_collate/durep.cds'"); my @files; my $next_id = 1; opendir(DIR, $opt_collate) or doAbort("Unable to open dir '$opt_collate': $!"); foreach(readdir(DIR)) { # Skip unless a .ds file next unless(/\.ds$/); push @files, $_; } closedir(DIR); foreach my $file (sort @files) { my $id = $next_id++; my %temp; tie %temp, 'MLDBM', "$opt_collate/$file", O_RDONLY, 0640 or doAbort("Unable to tie file '$opt_collate/$file'"); my %data = (%{$temp{DATA}}); $data{FILENAME} = $file; $data{SIZE} = $temp{1}->{SIZE}; $data{ID} = $id; $data{PATH} = $temp{1}->{NAME}; $db{$id} = \%data; untie %temp; } untie %db; } sub printDir { my ($dir, $indent) = @_; my @entries; foreach my $entry (@{$dir->{CHILDREN}}) { $entry = $root_node->{$entry}; next if(defined $opt_hidesize && $entry->{SIZE} < $opt_hidesize); my $e = {}; $e->{NAME} = $entry->{NAME}; $e->{SIZE} = $entry->{SIZE}; $e->{TYPE} = $entry->{TYPE}; $e->{MTIME} = $entry->{MTIME}; $e->{CHILDREN} = $entry->{CHILDREN} if $entry->{CHILDREN}; push @entries, $e; } @entries = reverse sort sortBySize @entries unless $opt_nosort; foreach my $entry (@entries) { my $numofchars; my $percent = $dir->{SIZE} == 0 ? 0 : ($entry->{SIZE}/$dir->{SIZE})*100; print " " x $indent; print prettyFileSize($entry->{SIZE}); $numofchars = int ((30 / 100) * $percent); printf(" [%s%s] ", "#" x $numofchars, " " x (30-$numofchars)); printf("%6.2f%% ", $percent); printf("%s ", shortDate($entry->{MTIME})) if $opt_showdate; printf("%s%s\n", $entry->{NAME}, $entry->{TYPE} & $TYPE_DIR ? "/" : ""); if($entry->{TYPE} & $TYPE_DIR) { printDir($entry, $indent+1) if(!defined $opt_textdepth || ($opt_textdepth > $indent+1)); } } } sub processSizeOption { my ($size, $temp); if($_[0] =~ m/[bBkKmMgG]$/) { ($size, $temp) = $_[0] =~ m/^(.+)([bBkKmMgG])$/; } else { $size = $_[0]; } unless (defined $size && ($size =~ m/^\d+$/ || $size =~ m/^\d+\.\d+$/)) { doAbort("Malformed argument: $_[0]"); } if(defined $temp) { if($temp =~ m/^[kK]/) { return $size * 1024; } elsif ($temp =~ m/^[mM]/) { return $size * 1048576; } elsif ($temp =~ m/^[mM]/) { return $size * 1048576 * 1024; } return $size; } } sub prettyFileSize { my $val = $_[0]; my $dtype = "b"; if($val >= 1024) { $val /= 1024; $dtype = "K"; } if($val >= 1024) { $val /= 1024; $dtype = "M"; } if($val >= 1024) { $val /= 1024; $dtype = "G"; } if($dtype eq "b") { return sprintf("%6d%s", $val, $dtype); } else { return sprintf("%6.1f%s", $val, $dtype); } } sub shortDate { if($_[0] < (time - 31536000)) { return POSIX::strftime("%b %e %Y", localtime $_[0]); } return POSIX::strftime("%b %e %H:%M", localtime $_[0]); } sub sortBySize { return $a->{SIZE} <=> $b->{SIZE}; } ### End program with error message sub doAbort { warn "Error: $_[0]\n"; exit 1; } sub catchError { warn "Program interrupted.\n"; if($opt_savefile || $opt_loadfile) { untie %{$root_node}; } exit 1; } sub usage { print < save the results of the scan into this file -lf, --load-file= load the results of a scan from this file -d, --desc= give description of save file -c, --collate= collate save files in dir for web report Inclusion Options: -f, --files do not descend into subdirs, only report files -x, --one-file-system do not traverse file systems -cp, --collapse-path= hide entries below paths that match regexp -ep, --exclude-path= ignore paths that match regexp -cf, --coalesce-files=N[bkmg] coalesce files less than N Bytes/Kb/Mb/Gb into one entry (default Bytes) EOF exit 0; } durep-0.9/durep.cgi0000755000175000017500000002517610116703463015145 0ustar psirenpsiren00000000000000#!/usr/bin/perl -w ############################################################################# # durep - Disk Usage Report Generator # # # # Copyright (C) 2004 Damian Kramer (psiren@hibernaculum.net) # # # # You may distribute this program under the terms of the Artistic License. # # # # 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 # # Artistic License for more details. # ############################################################################# use MLDBM qw(DB_File Storable); use POSIX; use strict; ## Set these variables as appropriate. ## -------------------------------------------------- our $datadir = "/var/lib/durep"; # Filesystem path to the data files. our $css_file = "/durep/style.css"; # URL to CSS file our $bar_image = "/durep/bar.png"; # URL to image used in bar graph our $show_mtime = 1; # Show file modifcation time our $show_opts = 1; # Show options used ## -------------------------------------------------- ### NO USER-SERVICEABLE PARTS BEYOND THIS POINT ### our ($me, $data, @ancestors, @records, %input, $temp, $version, $top_node, $collated); our ($TYPE_FILE, $TYPE_DIR, $TYPE_EMPTY, $TYPE_COALESCED, $TYPE_COLLAPSED); $me = $ENV{SCRIPT_NAME}; $TYPE_FILE = 0; $TYPE_DIR = 1; $TYPE_EMPTY = 2; $TYPE_COALESCED = 4; $TYPE_COLLAPSED = 8; $version = "0.9"; $ENV{PATH} = "/bin:/usr/bin"; $top_node = {}; $collated = loadCollateFile($datadir); getInput(); if(%input) { fetchData(); displayData(); } else { displayList(); } # Return to system exit 0; sub getInput { if($ENV{REQUEST_METHOD} eq 'POST') { chomp($_ = ); } elsif($ENV{REQUEST_METHOD} eq 'GET') { $_ = $ENV{QUERY_STRING}; } else { return; } for $temp (split("&", $_)) { my ($var, $val) = split ('=', $temp); $val =~ s/\+/ /g; $val =~ s/%(..)/pack("H2", $1)/eg; if(defined $input{$var}) { $input{$var} .= "\0$val"; } else { $input{$var} = $val; } } } sub displayInput { print "Content-type: text/html\n\n"; my ($key, $val); print "Disk Usage Report"; print "

"; print ""; while(($key, $val) = each %input) { my $length = length $val; print "\n"; } print "
$key
[$length]
[$val]
"; print ""; } sub fetchData { my $root_node = {}; my $node; tie %{$root_node}, 'MLDBM', "$datadir/$collated->{$input{fid}}->{FILENAME}", O_RDONLY, 0640 or errorPage(); %{$data} = (%{$root_node->{DATA}}); %{$top_node} = (%{$root_node->{$input{nid}}}); $node = $top_node; while($node->{PARENT}) { my $tmp = {}; %{$tmp} = (%{$root_node->{$node->{PARENT}}}); unshift @ancestors, $tmp; $node = $tmp; } foreach $node (@{$top_node->{CHILDREN}}) { my $tmp = {}; %{$tmp} = (%{$root_node->{$node}}); push @records, $tmp; } @records = reverse sort sortBySize @records; untie %{$root_node}; } sub displayList { print "Content-type: text/html\n\n"; my $hash = sortCollateFile($collated); my ($key, $val); print qq{ Disk Usage Report

Disk Usage Report

}; foreach my $host (keys %{$hash}) { print "\n"; my $flip = 0; foreach my $e (reverse sort sortByDate @{$hash->{$host}}) { if($flip) { print ""; } else { print ""; } $flip = !$flip; print ""; print ""; printf "", $e->{DESC} || "-"; printf "", prettyFileSize($e->{SIZE}); printf "", prettyNum($e->{DIR_COUNT}); printf "", prettyNum($e->{FILE_COUNT}); printf "", prettyDate($e->{LAST_UPDATE}); print "\n"; } } print qq{
 PathDescriptionSizeDirsFilesDate
$host
 $e->{PATH}%s%s%s%s%s
}; } sub displayData { print "Content-type: text/html\n\n"; my ($key, $val); print qq{ Disk Usage Report ($data->{HOSTNAME})

Disk Usage Report ($data->{HOSTNAME})

"; printf "", prettyFileSize($top_node->{SIZE}); print "
[Home] }; foreach my $tmp (@ancestors) { printf "$tmp->{NAME}%s", $tmp->{ID}, (($tmp->{NAME} =~ m|\/$|) ? "" : "/"); } print "$top_node->{NAME}%s
"; print "\n"; print ""; print ""; print "" if $show_mtime; print ""; my $flip = 0; foreach my $node (@records) { my $percent = $top_node->{SIZE} ? ($node->{SIZE}/$top_node->{SIZE}*100) : 0; if($flip) { print ""; } else { print ""; } $flip = !$flip; print "", $percent); if($node->{TYPE} & $TYPE_DIR) { if($node->{TYPE} & $TYPE_EMPTY) { print ""; printf("", shortDate($node->{MTIME})) if $show_mtime; print ""; } else { printf("", $node->{DCOUNT}); printf("", $node->{FCOUNT}); printf("", shortDate($node->{MTIME})) if $show_mtime; print ""; } } elsif($node->{TYPE} & $TYPE_COALESCED) { printf "", $node->{FCOUNT}; printf("", shortDate($node->{MTIME})) if $show_mtime; print ""; } else { print ""; printf("", shortDate($node->{MTIME})) if $show_mtime; print ""; } print "\n"; } print "
SizePercentageDirsFilesModifiedFile
"; print prettyFileSize($node->{SIZE}); print ""; print barChart($percent); printf("%4.2f%%00%s$node->{NAME}/%d%d%s$node->{NAME}/ %d%s$node->{NAME}  %s$node->{NAME}
\n"; print "
"; printf "", scalar localtime($data->{LAST_UPDATE}); print ""; print "
%sGenerated by durep v. $version
\n"; if($show_opts && $data->{OPTIONS}) { print "
    "; print "
  • Scanning files only.
  • " if $data->{OPTIONS}->{FILES}; print "
  • Restricting to single filesystem.
  • " if $data->{OPTIONS}->{ONEFILESYSTEM}; printf "
  • Collapsing paths matching %s.
  • ", $data->{OPTIONS}->{COLLAPSEPATH} if $data->{OPTIONS}->{COLLAPSEPATH}; printf "
  • Excluding paths matching %s.
  • ", $data->{OPTIONS}->{EXCLUDEPATH} if $data->{OPTIONS}->{EXCLUDEPATH}; printf "
  • Coalescing files below %s.
  • ", prettyFileSize($data->{OPTIONS}->{COALESCEFILES}) if $data->{OPTIONS}->{COALESCEFILES}; print "
"; } print ""; } ### All this does is create an HTML table bar graph type thingy. sub barChart { my $percent = int($_[0]*2+0.5); my $rv = "
"; if($percent) { $rv .= ""; } else { $rv .= " "; } $rv .= "
"; return $rv; } # Generates a human readable file size string sub prettyFileSize { my $val = $_[0]; my $dtype = "b"; if($val >= 1024) { $val /= 1024; $dtype = "K"; } if($val >= 1024) { $val /= 1024; $dtype = "M"; } if($val >= 1024) { $val /= 1024; $dtype = "G"; } if($dtype eq "b") { return sprintf("%d%s", $val, $dtype); } else { return sprintf("%.1f%s", $val, $dtype); } } sub prettyDate { return POSIX::strftime("%T %a %b %e %Y", localtime $_[0]); } sub shortDate { if($_[0] < (time - 31536000)) { return POSIX::strftime("%b %e %Y", localtime $_[0]); } return POSIX::strftime("%b %e %H:%M", localtime $_[0]); } sub prettyNum { my $r = reverse shift; $r =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; return scalar reverse $r; } sub sortBySize { return $a->{SIZE} <=> $b->{SIZE}; } sub sortByDate { return $a->{LAST_UPDATE} <=> $b->{LAST_UPDATE}; } sub sortByPath { return $a->{PATH} cmp $b->{PATH}; } sub loadCollateFile { my $dir = shift; my %db; my $r; tie %db, 'MLDBM', "$dir/durep.cds", O_RDONLY, 0640 or errorPage(); foreach my $key (keys %db) { $r->{$key} = \%{$db{$key}}; } untie %db; return $r; } sub sortCollateFile { my $hash = shift; my $r; foreach my $key (keys %{$hash}) { my $hostname = $hash->{$key}->{HOSTNAME}; push @{$r->{$hostname}}, \%{$hash->{$key}}; } return $r; } sub errorPage { print "Content-type: text/html\n\n"; print qq{ Disk Usage Report

Durep encountered an error!

Please check that you have collated the files and that the permissions on them are correct.

}; exit 0; } durep-0.9/durep.pod0000644000175000017500000001303710116674427015162 0ustar psirenpsiren00000000000000=head1 NAME durep - disk usage report generator =head1 SYNOPSIS durep [OPTIONS]... [DIRECTORY] =head1 DESCRIPTION B creates disk usage reports with bar graphs, allowing one to easily deduce which directories are using the most space. Although B can produce text output similar to du, its real power lies in the ability to store reports in a file, which can then be viewed as a web page with the supplied cgi script. =head1 OPTIONS Options are groubed into three distinct sections. =head2 Text Output Options These options are for controlling the text report output. =over 7 =item B<-td, --text-depth>=I Limit text report on directories to depth I. No directories below this level will be shown in the report. =item B< -hs, --hide-size>=I Do not display entries using I Bytes/Kb/Mb/Gb or less (default Bytes). This is to reduce clutter in the reports. It allows you to remove small files from the text report. =item B< -sd, --show-date> Display the modification date of the file or directory in the report. =item B<-ns, --nosort> Do not sort results by size. Leaves results in the order in which they were scanned, which is highly dependant on the filesystem. =item B<-q, --quiet> Do not produce text output. This stops the creation of a text report, and is useful when you are only interested in generating a save-file for use with the web report. =back =head2 File Options These options control load and save files. =over 7 =item B<-sf, --save-file>=I Save the results of the scan into this file. This can be loaded for a text report, but is generally used by the cgi script to display web reports. The filename should end in I<.ds> (it is appended if it does not). =item B<-lf, --load-file>=I Load the results of a scan from this file. This takes the place of scanning a directory. Inclusion options (described below) will not take effect if this option is used. =item B<-d, --desc>=I Give a description to be stored in the save-file. This is displayed on the web report summary page. =item B<-c, --collate=>=I Collate the save-files in the given directory. This creates a I file, which is used by the cgi script to manage and display save-files. See B section below for more detail. =back =head2 Inclusion Options These options control which directories and files should be included in the report. =over 7 =item B<-f, --files> Do not descend into sub-directories, only report files. =item B<-x, --one-file-system> Do not traverse file systems. This is similar to the B<-x> option for du, allowing easy checking of an entire filesystem such as /. =item B<-cp, --collapse-path=>I Hide entries below paths that match I. This allows you to conceal the contents of certain directories in the report. You may wish perhaps to show home directories in a report but not show their content in which case you could use the option C<-cp '/home'>. =item B<-ep, --exclude-path>=I Ignore paths that match I. This works in a smilar manner to C<-cp> above, except it excludes the directory from the scan itself. =item B<-cf, --coalesce-files=>=I Coalesces entries for files below the given size into one entry. This is useful for reducing clutter in reports. =back =head1 WEB REPORTS Since version 0.9, durep no longer directly generates html files for its web reports. It now uses a cgi script that reads data from save-files. The script will handle multiple save-files, potentially from multiple hosts, so you can consolidate your reports into one place. Copying save-files from other hosts is left as an exercise for the reader. It is necessary to collate the save-files before viewing them via the cgi script. This process creates the file C which contains metadata about all of the save-files. From this a summary page is shown where you can choose which report you wish to view. The collation must be done any time a save-file is added or overwritten. The cgi-script has some configurable variables at the top. These tell the script where to look for the css file and the graphic used for the bar graphs. There are also options to set whether the modification date, and/or the options used to create the save-file should be shown. These are both set to 1 by default. As always, you should take care when installing the cgi script. I've done my best, but I make no guarantees about its security. It would probably be unwise to allow this script to be accessed from the internet at large. =head1 EXAMPLES =over 3 =item 1. B This would print the directory tree starting from the current directory to depth 2. =item 2. B This might be useful for keeping a check on the mail directory. The C<-f> switch tells durep to just scan files and not descend into directories. =item 3. B This more complicated version does the following. It scans the root filesystem only, collapses the contents of any paths begining F or F and skips the contents of the F directory. It saves the output of this report into the file I. No text report is produced. =item 4. B This reads the save-file I and produces a text report from it, hiding any files below 1 megabyte. =item 5. B This collates any save-files in I. =back =head1 SEE ALSO du(1), perl(1) =head1 AUTHOR Damian Kramer Epsiren@hibernaculum.netE durep-0.9/style.css0000644000175000017500000000314710116674427015212 0ustar psirenpsiren00000000000000a:hover { background-color: #DDDDDD; } body { text-align: center; background-color: #ffffff; } div.tbar { text-align: left; width: 100%; background-color: #EEEEEE; border-top: solid 1px #DDDDDD; border-bottom: solid 1px #DDDDDD; } div.bbar { text-align: left; width: 100%; background-color: #EEEEEE; border-top: solid 1px #DDDDDD; border-bottom: solid 1px #DDDDDD; } div.bbar * { font-size: 90%; } div.options { text-align: left; margin: 2em; background-color: #EEEEEE; border: solid 1px #DDDDDD; } .light { background-color: #ffffff; } .mid { background-color: #f6f6f6; } .dark { background-color: #f0f0f0; } .tbar table { width: 100%; padding-left: 2pt; font-weight: bold; } .bbar table { width: 100%; padding-left: 2pt; } table.list { margin: 2em 0px; border: solid 1px #CCCCCC; margin-left: auto; margin-right: auto; } table.list td { text-align: left; padding: 2px 5px; } table.list th { text-align: left; padding: 2px 5px; text-align: left; background-color: #DDDDDD; } table.report { margin: 2em 0px; border: solid 1px #CCCCCC; text-align: left; margin-left: auto; margin-right: auto; } table.report td { padding: 2px 5px; } table.report th { padding: 2px 5px; text-align: left; background-color: #DDDDDD; } .graph { padding: 0px; background-color: #eeeeee; width: 200px; height: 15px; } th.right { text-align: right; } td.right { text-align: right; } span.dir { font-weight: bold; } span.empty { color: #006600; font-weight: bold; } span.coalesced { color: #880000; font-weight: bold; }