Here is a very fast Perl program that will find cruft in /etc/ and its subdirectories. It is very fast because it reads the CONTENTS files just once and puts possible target files (from /etc) in a hash.
It works fine as is, but it could use a bit more work. The list of IGNORE files could probably be larger and I want to add a feature so it can read in additional ignore files both from the command line and from a file so each person can customize which files to ignore on their own system without modifying the code.
It has two limitations, both of which are easy to fix. The first is that it will fail for filenames with spaces in them in the CONTENTS files. Second, it only searches for beginning matches for files from the IGNORE list which makes it very easy to exclude directories. There should probably be an IGNORE_DIR list that still behaves this way and an IGNORE_FILE list that only ignores exact matches.
I am posting it now because it works and is very fast but I'm tired now and may not get around to adding those other features right away.
Code: Select all
#!/usr/bin/perl -w
use strict;
use Getopt::Long;
my $TARGET = "/etc";
my $DB_DIR = "/var/db/pkg";
my @IGNORE = ("/etc/OLD_ETC",
"/etc/splash",
"/etc/runlevels",
"/etc/ssl/certs",
"/etc/X11/xorg.conf",
"/etc/group",
"/etc/shadow",
"/etc/gshadow-",
"/etc/fstab",
"/etc/localtime",
"/etc/make.conf",
"/etc/passwd",
"/etc/resolv.conf",
"/etc/hosts",
);
my $ME = $0; $ME =~ s{.*/}{};
my $USAGE = <<USAGE;
Usage: $ME [options]
Options:
--target DIR Look for cruft in DIR (default: /etc)
--db_dir DIR Look for package files in DIR (default: /var/db/package)
--pretend Does nothing yet.
--verbose Prints a few summary lines.
--help Print this usage.
USAGE
my $PRETEND;
my $VERBOSE;
GetOptions(
"help" => sub { print $USAGE; exit},
"pretend" => \$PRETEND,
"verbose" => \$VERBOSE,
"target" => \$TARGET,
"db_dir" => \$DB_DIR,
) or die $USAGE;
#--- Grab valid files from all contents files ---
my %VALID;
for my $dir (<$DB_DIR/*>) {
for my $package (<$dir/*>) {
open(CONTENTS, "$package/CONTENTS") or next;
while (<CONTENTS>) {
m/^(?:obj|dir) ($TARGET\S*)/o and $VALID{$1}++;
}
close CONTENTS;
}
}
$VERBOSE and print "Found @{[scalar keys %VALID]} valid file names.\n";
my @FILES = `find $TARGET -print`;
chomp(@FILES);
$VERBOSE and print "Found @{[scalar @FILES]} target files.\n";
my @CRUFT;
FILE:
for my $file (@FILES) {
$VALID{$file} and next FILE;
for my $re (@IGNORE) {
$file =~ /^$re/ and next FILE;
}
push @CRUFT, $file;
}
print join "\n", @CRUFT, "\n";
$VERBOSE and print "Found @{[scalar @CRUFT]} cruft files.\n";