#!/usr/bin/perl -w
#use strict;
use Getopt::Std;
my %opts;
my ($all,$files,$sizeopt,$perms,$provides) = (0,0,0,0,0);
my ($oldrpms,$newrpms);
#opts -a all -f files -s file size -p file perms -l dynamic linkages -r requires/provides -o old tree -n new tree
getopts('afsplro:n:',\%opts);
if(defined($opts{a})){ $all = 1; print "all\n";};
if(defined($opts{f})){ $files = 1; print "files\n"; };
if(defined($opts{s})){ $sizeopt = 1; print "size\n";};
if(defined($opts{p})){ $perms = 1; print "perms\n";};
if(defined($opts{l})){ $links = 1; print "links\n";};
if(defined($opts{r})){ $provides = 1; print "provides\n"; };
if(defined($opts{o})){ if (-d $opts{o}) { $oldrpms = $opts{o}; print "old $oldrpms\n"; }; };
if(defined($opts{n})){ if (-d $opts{n}) { $newrpms = $opts{n}; print "new $newrpms\n"; }; };
if(! ($all or $files or $sizeopt or $links or $perms or $provides)){
print "$0\nOptions:\n-a\tall\n-f\tfiles\n-s\tfile size\n-p\tfile perms\n-l\tdynamic linkages\n-r\trequires/provides\n-o\t
old tree\n-n\t new tree\n";
exit;
}
#print %opts;
#print "\n";
if ($ARGV[0]){
print "Unknown extra argument: ";
foreach (@ARGV) {
print "$_ ";
}
print "\n";
print "$0\nOptions:\n-a\tall\n-f\tfiles\n-s\tfile size\n-p\tfile perms\n-l\tdynamic linkages\n-r\trequires/provides\n-o\t old tree\n-n\t new tree\n";
exit;
}
#print @oldrpms."\n";
#print @newrpms."\n";
if(!$oldrpms or !$newrpms)
{
exit;
}
#Point these to a directory of RH and rebuilt (aka new) RPMS
#$WB_RPMS = "/home/test/b8";
#$RH_RPMS = "/home/test/new";
#my $WB_RPMS = "/centos-3/build8/i386/RedHat/RPMS";
#my $RH_RPMS = "/centos-3/build6/i386/RedHat/RPMS";
# Size difference to trigger error, .1 = plus/minus 10%
my $SizeError=.10;
# Working directories
# Careful where these point, they get a "rm -rf" cleaning!
my $oldTMP="/tmp/old-$$";
my $newTMP="/tmp/new-$$";
my $rpm;
opendir(DIR,$newrpms);
my @allrpms= grep (/rpm$/, readdir(DIR) );
closedir(DIR);
foreach $rpm (sort @allrpms){
my $rpmold=$rpm;
my $rpmoldf=$rpm;
if (! (-e "$oldrpms/$rpm") ) {
#try without .centos.x
my $fname = $rpm;
$fname =~ s/centos\.[0-9]//;
print $fname;
if (! (-e "$oldrpms/$fname")) {
print "\nNo equivalent original for $rpm \t trying glob\n";
$fname= $rpm;
$fname =~ s/(.*)-.*-.*/$1-/;
# print "fname is $fname\n";
my $found = 0;
#print "trying $oldrpms/$fname*\n";
my @globmatch = glob("$oldrpms/$fname*");
while ($rpmold = shift @globmatch) {
#while($rpmold = glob("$oldrpms/$fname*")){
my $try = $rpmold;
# print "\nglob got $try\n ";
$try =~ s/.*\///;
$try =~ s/(.*)-.*-.*/$1-/;
if($try eq $fname)
{
#this is the one
$rpmoldf = $rpmold;
$rpmoldf =~ s/.*\///;
$found = 1;
while($rpmold = glob("$oldrpms/$fname*")){};
last;
}
}
if(!$found)
{
next;
}
}
}
if($rpm =~ /^kernel/)
{
next;
}
if($rpm eq $rpmoldf)
{
# only doing difficult ones
# next;
}
else
{
print "$rpm >> $rpmoldf\n";
}
if($all)
{
printf ("%-50s\t", $rpm);
}
$fileproblems=0; $libproblems=0;
if ($all or $files or $sizeopt or $perms or $links){
# Initialize
%allfiles=(); %oldhash=(); %newhash=();
system("rm -rf $oldTMP;mkdir $oldTMP");
system("rm -rf $newTMP;mkdir $newTMP");
# Extract RPMS
system("cd $oldTMP/; rpm2cpio $oldrpms/$rpmoldf | cpio -id --no-absolute-filenames --quiet");
system("cd $newTMP/; rpm2cpio $newrpms/$rpm | cpio -id --no-absolute-filenames --quiet");
# Parse name/size info
@oldnamesize=`cd $oldTMP/; find ./ -printf "%p:_:%s:_:%U:_:%G:_:%04m:_:\n"`;
@newnamesize=`cd $newTMP/; find ./ -printf "%p:_:%s:_:%U:_:%G:_:%04m:_:\n"`;
for ($i=0;$i<@oldnamesize;$i++){
($file,$size,$uid,$gid,$perms)=split(/:_:/,$oldnamesize[$i]);
#print "old: $file,$size,$uid,$gid,$perms\n";
$oldhash{"$file"}=$size;
$olduid{"$file"}=$uid;
$oldgid{"$file"}=$gid;
$oldperms{"$file"}=$perms;
$allfiles{"$file"}=1;
}
for ($i=0;$i<@newnamesize;$i++){
($file,$size,$uid,$gid,$perms)=split(/:_:/,$newnamesize[$i]);
#print "new: $file,$size,$uid,$gid,$perms\n";
$newhash{"$file"}=$size;
$newuid{"$file"}=$uid;
$newgid{"$file"}=$gid;
$newperms{"$file"}=$perms;
$allfiles{"$file"}=1;
}
if($all or $files or $sizeopt)
{
# Print and tag file errors if appropraite
foreach $file (keys %allfiles){
if (!(exists $oldhash{"$file"})){
if(!$fileproblems){printf ("%-50s\t", $rpm);};
print "\n\tPackage contains extra file: $file";
$fileproblems=1;
}
elsif (!(exists $newhash{"$file"})){
if(!$fileproblems){printf ("%-50s\t", $rpm);};
print "\n\tPackage is missing file: $file";
$fileproblems=1;
}
else {
if ($oldhash{"$file"}>0){
$ratio=($newhash{"$file"})/($oldhash{"$file"})}
else{
$ratio=1-$newhash{"$file"}; # Negative ratios indicate filesize vs. zero in RedHat
}
if($sizeopt){
print "size: $sizeopt\n";
if (($ratio-1)*($ratio-1)>($SizeError*$SizeError)) {
if(!$fileproblems){printf ("%-50s\t", $rpm);};
printf("\n\tSize Ratio: %3.2f on file:%s",$ratio,$file);
$fileproblems=1;
};
}
}
if($all or $perms)
{
if (!$fileproblems){
if($olduid{"$file"} ne $newuid{"$file"}) {
printf ("%-50s\t", $rpm);
printf("\n\tUid Diff $olduid{\"$file\"} : $newuid{\"$file\"} on $file");
$fileproblems=1;
}
if ($oldgid{"$file"} ne $newgid{"$file"}) {
printf ("%-50s\t", $rpm);
printf("\n\tGid Diff $oldgid{\"$file\"} : $newgid{\"$file\"} on $file");
$fileproblems=1;
}
if ($oldperms{"$file"} ne $newperms{"$file"}) {
printf ("%-50s\t", $rpm);
printf("\n\tPerms Diff $oldperms{\"$file\"} : $newperms{\"$file\"} on $file");
$fileproblems=1;
}
}
}
}
}
if($all or $links)
{
# Select executable files
@oldexec=`cd $oldTMP/; find ./ -perm +111 -type f -print`;
# Check library dependencies
foreach $executable (@oldexec) {
chop $executable;
# Move on if no equivalent binary exists in rebuild
if (! (-e "$newTMP/$executable") ) {next;}
# Get shared libs
@oldlibs=`ldd $oldTMP/$executable`;
@newlibs=`ldd $newTMP/$executable`;
# Clean up ldd output
$oldlibline=join('',(sort @oldlibs));
$newlibline=join('',(sort @newlibs));
$oldlibline=~ s/=>(.*)(\n)/\n/g; $oldlibline=~ s/(\s+)/ /g;
$newlibline=~ s/=>(.*)(\n)/\n/g; $newlibline=~ s/(\s+)/ /g;
@oldlibs=split(/ /,$oldlibline);
@newlibs=split(/ /,$newlibline);
# Print and tag library errors
foreach $lib (@oldlibs){
$searchpat=$lib;
$searchpat =~ s/\+/\\\+/g;
$searchpat =~ s/\./\\\./g;
unless ($newlibline =~ (/$searchpat/)){
if(!$libproblems and !$fileproblems){printf ("%-50s\t", $rpm);};
print "\n\tMissing link in $executable:\t$lib";$libproblems=1;
}
}
foreach $lib (@newlibs){
$searchpat=$lib;
$searchpat =~ s/\+/\\\+/g;
$searchpat =~ s/\./\\\./g;
unless ($oldlibline =~ (/$searchpat/)){
if(!$libproblems and !$fileproblems){printf ("%-50s\t", $rpm);};
print "\n\tExtra link in $executable:\t$lib";$libproblems=1;
}
}
}
}
}
if($all or $provides)
{
# sort out provides and requires
@oldrequires = `rpm -qp --requires $newrpms/$rpm`;
@newrequires = `rpm -qp --requires $oldrpms/$rpmoldf`;
while ($oldrequire = shift(@oldrequires))
{
if($newrequire = shift(@newrequires))
{
chomp($oldrequire);
chomp($newrequire);
# print "$oldrequire : $newrequire\n";
if ($oldrequire gt $newrequire)
{
# extra one in newrequire
if(!$fileproblems and !$libproblems and !$fileproblems){printf ("%-50s\t", $rpm);};
print "\n\tExtra require $newrequire";$fileproblems=1;
unshift(@oldrequires,$oldrequire);
}
elsif($oldrequire lt $newrequire)
{
# one missing in newrequire
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tMissing require $oldrequire";$fileproblems=1;
unshift(@newrequires,$newrequire);
}
}
else
{
# end of new array
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tMissing require $oldrequire";$fileproblems=1;
}
}
if(shift(@newrequires)){
# extra in the new array
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tExtra require $newrequire";$fileproblems=1;
}
@oldprovides = `rpm -qp --provides $newrpms/$rpm`;
@newprovides = `rpm -qp --provides $oldrpms/$rpmoldf`;
while ($oldprovide = shift(@oldprovides))
{
if($newprovide = shift(@newprovides))
{
chomp($oldprovide);
chomp($newprovide);
# print "$oldprovide : $newprovide\n";
if ($oldprovide gt $newprovide)
{
# extra one in newprovide
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tExtra provide $newprovide";$fileproblems=1;
unshift(@oldprovides,$oldprovide);
}
elsif($oldprovide lt $newprovide)
{
# one missing in newprovide
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tMissing provide $oldprovide";$fileproblems=1;
unshift(@newprovides,$newprovide);
}
}
else
{
# end of new array
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tMissing provide $oldprovide";$fileproblems=1;
}
}
if(shift(@newprovides)){
# extra in the new array
if(!$fileproblems and !$libproblems){printf ("%-50s\t", $rpm);};
print "\n\tExtra provide $newprovide";$fileproblems=1;
}
}
if ($fileproblems || $libproblems) { print "\n";} else { if($all) { print "MATCH\n"; }; }
}