#!/usr/bin/perl ######################################################## # URPM Repos Closure Checker 1.3.1 for Linux # A tool for checking closure of a set of RPM packages # # Copyright (C) 2012 ROSA Laboratory # Written by Andrey Ponomarenko # # PLATFORMS # ========= # Linux (ROSA, Mandriva) # # REQUIREMENTS # ============ # - urpmi # - Perl 5 (>=5.8) # - Wget # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation. # # 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 # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ######################################################## use Getopt::Long; Getopt::Long::Configure ("posix_default", "no_ignore_case"); use Cwd qw(abs_path cwd); use File::Path qw(mkpath rmtree); use File::Temp qw(tempdir); use File::Copy qw(copy move); use Data::Dumper; use Locale::gettext; use strict; my $TOOL_VERSION = "1.3.1"; my $CmdName = get_filename($0); my ($Help, $ShowVersion, $RPMlist, $RPMdir, $StaticMode, $DynamicMode, $CheckRelease, $CheckSignature, $SelectRepos, $NoClean, $Root, $HDlist, $FileDeps, $ResDir, $AddRPMs); textdomain("urpm-tools"); sub gettext_(@) { my ($Str, @Params) = @_; if(not $Str) { return ""; } $Str = gettext($Str); foreach my $N (1 .. $#Params+1) { my $P = $Params[$N-1]; $Str=~s/\[_$N\]/$P/g; } return $Str; } my $ShortUsage = gettext_("URPM Repos Closure Checker [_1] for Mandriva Linux A tool for checking closure of a set of RPM packages Copyright (C) 2012 ROSA Laboratory License: GNU GPL Usage: [_2] [options] Example: [_2] --hdlist=hdlist.txt More info: [_2] --help\n", $TOOL_VERSION, $CmdName); if($#ARGV==-1) { print $ShortUsage."\n"; exit(0); } GetOptions("h|help!" => \$Help, "v|version!" => \$ShowVersion, "l|list=s" => \$RPMlist, "d|dir=s" => \$RPMdir, "hdlist=s" => \$HDlist, "add=s" => \$AddRPMs, "file-deps=s" => \$FileDeps, "s|static!" => \$StaticMode, "dynamic!" => \$DynamicMode, "r|check-release!" => \$CheckRelease, "sign|check-signature!" => \$CheckSignature, "media=s" => \$SelectRepos, "noclean!" => \$NoClean, "root=s" => \$Root, "o|res=s" => \$ResDir ) or ERR_MESSAGE(); my %EXIT_CODES = ( "SUCCESS" => 0, "ERROR" => 1, "FAILED" => 2 ); my $HelpMessage = gettext_(" NAME: URPM Repos Closure Checker 1.0 for Mandriva Linux A tool for checking closure of a set of RPM packages USAGE: [_1] --hdlist=hdlist.txt [_1] --hdlist=http://mirror.yandex.ru/mandriva/.../synthesis.hdlist.cz [_1] --dir=rpms/ --static --file-deps=file-deps.txt [_1] --list=list.txt --dynamic OPTIONS: -h|-help Print this help. -v|-version Print version information. -hdlist Path or URL of HDlist (synthesis) to check. -d|-dir The directory with RPM packages to check. -l|-list The list of packages to check. -add|-update The directory with RPM packages that should be added to the repository or updated. -file-deps Read file-deps to ignore some unresolved dependencies. -s|-static Check statically if all required dependencies are satisfied by provided dependencies in the set of RPM packages. -dynamic Install a set of RPM packages to the local chroot and check if extra packages were installed. -r|-check-release Check installation media (DVD). -sign|-check-signature Validate package signatures. -noclean Do not clean urpmi cache. -root Where to install packages. Default: /tmp/... EXIT CODES: 0 - Suceess. The tool has run without any errors non-zero - Failed or the tool has run with errors. In particular: 1 - Failed to run the tool 2 - Discovered dependency problems \n", $CmdName); sub HELP_MESSAGE() { print $HelpMessage; } sub ERR_MESSAGE() { print $ShortUsage; exit(1); } my %Cache; my $RPM_CACHE = "/var/cache/urpmi/rpms"; my $TMP_DIR = tempdir(CLEANUP=>1); my %InstalledPackage; my %RequiredBy; my $TEST_MEDIA = "test_media"; my %Packages; my %BrokenSignature; my %InstallFailed; my $RESULTS_DIR = "repoclosure_reports"; sub appendFile($$) { my ($Path, $Content) = @_; return if(not $Path); if(my $Dir = get_dirname($Path)) { mkpath($Dir); } open(FILE, ">>".$Path) || die gettext_("can't open file \'[_1]\': [_2]\n", $Path, $!); print FILE $Content; close(FILE); } sub writeFile($$) { my ($Path, $Content) = @_; return if(not $Path); if(my $Dir = get_dirname($Path)) { mkpath($Dir); } open (FILE, ">".$Path) || die gettext_("can't open file \'[_1]\': [_2]\n", $Path, $!); print FILE $Content; close(FILE); } sub readFile($) { my $Path = $_[0]; return "" if(not $Path or not -f $Path); open (FILE, $Path); local $/ = undef; my $Content = ; close(FILE); return $Content; } sub get_filename($) { # much faster than basename() from File::Basename module if($_[0]=~/([^\/\\]+)[\/\\]*\Z/) { return $1; } return ""; } sub get_dirname($) { # much faster than dirname() from File::Basename module if($_[0]=~/\A(.*?)[\/\\]+[^\/\\]*[\/\\]*\Z/) { return $1; } return ""; } sub searchRPMs($) { my $Path = $_[0]; if(not $Path or not -d $Path) { return (); } my @RPMs = split("\n", `find $Path -type f -name "*.rpm"`); # -maxdepth 1 return sort {lc($a) cmp lc($b)} @RPMs; } sub addMedia($) { my $Dir = $_[0]; if(not $Dir or not -d $Dir) { return; } my %Media = map {$_=>1} split(/\n+/, `urpmq --list-media`); if($Media{$TEST_MEDIA}) { removeMedia(); } $Dir = abs_path($Dir); system("/usr/sbin/urpmi.addmedia $TEST_MEDIA $Dir"); system("/usr/sbin/urpmi.update $TEST_MEDIA"); } sub removeMedia() { system("/usr/sbin/urpmi.removemedia $TEST_MEDIA"); } sub installPackage($) { my $Package = $_[0]; my $Cmd = "/usr/sbin/urpmi"; if($CheckRelease) { # from CD or DVD $Cmd .= " --media=$TEST_MEDIA"; } elsif($SelectRepos) { if(-d $SelectRepos) { $Cmd .= " --media=$TEST_MEDIA"; } else { $Cmd .= " --media=$SelectRepos"; } } # create root where to install packages if(not -d $TMP_DIR."/root") { mkpath($TMP_DIR."/root"); } if(not $CheckRelease) { $Cmd .= " --no-install"; } if($Root) { $Cmd .= " --root=\"$Root\""; } else { $Cmd .= " --root=\"$TMP_DIR/root\""; } $Cmd .= " --noclean --auto --force"; $Cmd .= " $Package"; print "Running $Cmd\n"; my $LogPath = $TMP_DIR."/ilog.txt"; system($Cmd." >$LogPath 2>&1"); my $Log = readFile($LogPath); appendFile("$RESULTS_DIR/install-log.txt", $Log); $Log=~s/The following packages have to be removed (.|\n)*\Z//g; if($Log=~/ (unsatisfied|conflicts with|missing) ([\w\-\/]*)/i) { my ($Reason, $Dep) = ($1, $2); $InstallFailed{getPName($Package)}=1; print " FAILED: due to $Reason $Dep\n"; } if($CheckRelease) { # installed while($Log=~s/(installing\s+)([^\/\s]+\.rpm)(\s|\Z)/$1/) { my $RpmName = $2; print " $RpmName\n"; } } else { # downloaded while($Log=~s/(\/)([^\/\s]+\.rpm)(\s|\Z)/$1$3/) { my $RpmName = $2; print " $RpmName\n"; $RequiredBy{getPName($RPM_CACHE."/".$RpmName)}=getPName($Package); } } } sub get_RPMname($) { my $Path = $_[0]; my $Name = get_filename($Path); if($Cache{"get_RPMname"}{$Name}) { return $Cache{"get_RPMname"}{$Name}; } if(not $Path or not -f $Path) { return ""; } return ($Cache{"get_RPMname"}{$Name} = `rpm -qp --queryformat \%{name} \"$Path\"`); } sub sepDep($) { my $Dep = $_[0]; if($Dep=~/\A(.+?)(\s+|\[)(=|==|<=|>=|<|>)\s+(.+?)(\]|\Z)/) { my ($N, $O, $V) = ($1, $3, $4); # canonify version (1:3.2.5-5:2011.0) return ($N, $O, $V); } else { return ($Dep, "", ""); } } sub showDep($$$) { my ($N, $O, $V) = @_; if($O and $V) { return $N." ".$O." ".$V; } else { return $N } } sub sepVersion($) { my $V = $_[0]; if($V=~/\A(.+)(\-[^\-\:]+)(\:[^\:]+|)\Z/) { # 3.2.5-5:2011.0 return ($1, $2, $3); } return ($V, "", ""); } sub simpleVersion($) { # x.y.z-r:n to x.y.z.r.n my $V = $_[0]; $V=~s/[\-:]/\./g; # -5:2011.0 $V=~s/[a-z]+/\./ig; # 10-12mdk $V=~s/\.\Z//g; return $V; } sub formatVersions(@) { # V1 - provided # V2 - required my ($V1, $V2) = @_; my ($E1, $E2) = (); if($V1=~s/\A([^\-\:]+)\://) { $E1 = $1; } if($V2=~s/\A([^\-\:]+)\://) { $E2 = $1; } my ($V1_M, $V1_R, $V1_RR) = sepVersion($V1); my ($V2_M, $V2_R, $V2_RR) = sepVersion($V2); if(not $V2_RR) { $V1_RR = ""; } if(not $V2_R) { $V1_R = ""; } $V1 = $V1_M.$V1_R.$V1_RR; $V2 = $V2_M.$V2_R.$V2_RR; if(defined $E1 and defined $E2) { $V1 = $E1.".".$V1; $V2 = $E2.".".$V2; } return (simpleVersion($V1), simpleVersion($V2)); } sub cmpVersions($$) { # compare two versions # 3.2.5-5:2011.0 # NOTE: perl 5.00503 and 5.12 my ($V1, $V2) = formatVersions(@_); return 0 if($V1 eq $V2); my @V1Parts = split(/\./, $V1); my @V2Parts = split(/\./, $V2); for (my $i = 0; $i <= $#V1Parts && $i <= $#V2Parts; $i++) { my $N1 = $V1Parts[$i]; my $N2 = $V2Parts[$i]; if(defined $N1 and not defined $N2) { return 1; } elsif(not defined $N1 and defined $N2) { return -1; } if(my $R = cmpNums($N1, $N2)) { return $R; } } return -1 if($#V1Parts < $#V2Parts); return 1 if($#V1Parts > $#V2Parts); return 0; } sub cmpNums($$) { my ($N1, $N2) = @_; # 00503 # 12 if($N1 eq $N2) { return 0; } while($N1=~s/\A0([0]*[1-9]+)/$1/) { $N2.="0"; } while($N2=~s/\A0([0]*[1-9]+)/$1/) { $N1.="0"; } return int($N1)<=>int($N2); } sub checkDeps($$$$) { my ($N, $O, $V, $Provides) = @_; if(not $O or not $V) { # requires any version return 1; } foreach my $OP (keys(%{$Provides})) { if(not $OP) { # provides any version return 1; } foreach my $VP (keys(%{$Provides->{$OP}})) { if($O eq "=" or $O eq "==") { if(cmpVersions($VP, $V)==0) { # requires the same version return 1; } } elsif($O eq "<=") { if(cmpVersions($VP, $V)<=0) { return 1; } } elsif($O eq ">=") { if(cmpVersions($VP, $V)>=0) { return 1; } } elsif($O eq "<") { if(cmpVersions($VP, $V)<0) { return 1; } } elsif($O eq ">") { if(cmpVersions($VP, $V)>0) { return 1; } } } } return 0; } sub checkSignature($) { my $Path = $_[0]; my $Info = `rpm --checksig $Path`; if($Info!~/ OK(\s|\Z)/) { $BrokenSignature{getPName($Path)}=1; return 0; } return 1; } sub checkRoot() { if(not -w "/usr") { print STDERR gettext_("ERROR: you should be root\n"); exit(1); } } sub readRPMlist($$) { my ($Path, $Type) = @_; if(not -f $Path) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $Path); exit(1); } my @RPMs = split(/\s+/, readFile($Path)); if($#RPMs==-1) { print STDERR gettext_("ERROR: the list of packages is empty\n"); exit(1); } if($Type eq "RPMs") { foreach my $P (@RPMs) { if($P!~/\.rpm\Z/) { print STDERR gettext_("ERROR: file \'[_1]\' is not RPM package\n", $P); exit(1); } elsif(not -f $P) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $P); exit(1); } } } return @RPMs; } sub checkRelease() { checkRoot(); if(not $RPMdir and not $RPMlist) { print STDERR gettext_("ERROR: --dir or --list option should be specified\n"); exit(1); } clearCache(); my @RPMs = (); if($RPMlist) { @RPMs = readRPMlist($RPMlist, "RPMs"); $RPMdir = get_dirname($RPMs[0]); if(not $RPMdir) { $RPMdir = "."; } } else { if(not -d $RPMdir) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $RPMdir); exit(1); } @RPMs = searchRPMs($RPMdir); } addMedia($RPMdir); foreach my $Path (@RPMs) { # add to cache if(not -f $RPM_CACHE."/".get_filename($Path)) { # copy($Path, $RPM_CACHE); } } foreach my $Path (@RPMs) { installPackage($Path); $Packages{get_filename($Path)} = 1; } removeMedia(); checkResult(); } sub dynamicCheck() { checkRoot(); if(not $RPMdir and not $RPMlist) { print STDERR gettext_("ERROR: --dir or --list option should be specified\n"); exit(1); } clearCache(); my @RPMs = (); if($RPMdir) { # --dir option if(not -d $RPMdir) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $RPMdir); exit(1); } @RPMs = searchRPMs($RPMdir); foreach my $Path (@RPMs) { # add to cache copy($Path, $RPM_CACHE); } if(-d $SelectRepos) { addMedia($SelectRepos); } foreach my $Path (@RPMs) { installPackage($Path); $Packages{get_RPMname($Path)} = 1; $Packages{get_filename($Path)} = 1; } if(-d $SelectRepos) { removeMedia(); } } elsif($RPMlist) { @RPMs = readRPMlist($RPMlist, "Names"); if(-d $SelectRepos) { addMedia($SelectRepos); } foreach my $Name (@RPMs) { installPackage($Name); $Packages{$Name} = 1; } if(-d $SelectRepos) { removeMedia(); } } checkResult(); } sub getPName($) { # package ID my $Path = $_[0]; if($RPMdir or not -f $Path) { # input: RPMs return get_filename($Path); } else { # input: RPM names return get_RPMname($Path); } } sub isInstalled($) { my $Name = $_[0]; if($InstallFailed{$Name}) { return 0; } if(not $CheckRelease) { if(not $InstalledPackage{$Name}) { return 0; } } return 1; } sub checkResult() { my (%ExtraPackages, %BrokenPackages) = (); foreach my $Path (searchRPMs($RPM_CACHE)) { # extra my $Name = getPName($Path); $InstalledPackage{$Name} = 1; if(not $Packages{$Name}) { $ExtraPackages{$Name} = $Path; } } foreach my $Name (keys(%Packages)) { # broken if(not isInstalled($Name)) { $BrokenPackages{$Name}=1; } } if(my @Names = sort {lc($a) cmp lc($b)} keys(%ExtraPackages)) { my $Report = gettext_("Extra Packages:\n\n"); foreach my $Name (@Names) { $Report .= $Name; if(my $Req = $RequiredBy{$Name}) { $Report .= gettext_(" (required by: [_1])", $Req); } $Report .= "\n"; } print $Report; writeFile("$RESULTS_DIR/extra-packages.txt", $Report); } if(my @Names = sort {lc($a) cmp lc($b)} keys(%BrokenPackages)) { my $Report = gettext_("Broken Packages:\n\n"); foreach my $Name (@Names) { $Report .= "$Name\n"; } print $Report; writeFile("$RESULTS_DIR/broken-packages.txt", $Report); } print gettext_("Report has been generated to:"); print "\n $RESULTS_DIR/extra-packages.txt\n $RESULTS_DIR/broken-packages.txt\n"; if(keys(%ExtraPackages) or keys(%BrokenPackages)) { exit($EXIT_CODES{"FAILED"}); } else { exit($EXIT_CODES{"SUCCESS"}); } } sub sigCheck() { if(not $RPMdir and not $RPMlist) { print STDERR gettext_("ERROR: --dir or --list option should be specified\n"); exit(1); } print gettext_("Checking RPMs ...\n"); my @RPMs = (); if($RPMdir) { if(not -d $RPMdir) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $RPMdir); exit(1); } @RPMs = searchRPMs($RPMdir); } elsif($RPMlist) { @RPMs = readRPMlist($RPMlist, "RPMs"); } foreach my $Path (@RPMs) { print gettext_("Checking [_1]\n", get_filename($Path)); if(not checkSignature($Path)) { print gettext_(" FAILED: invalid signature\n"); } } if(my @Names = sort {lc($a) cmp lc($b)} keys(%BrokenSignature)) { my $Report = gettext_("Broken Signature:\n\n"); foreach my $Name (@Names) { $Report .= "$Name\n"; } print $Report; writeFile("$RESULTS_DIR/report.txt", $Report); } print gettext_("Report has been generated to:"); print "\n $RESULTS_DIR/report.txt\n"; if(keys(%BrokenSignature)) { exit($EXIT_CODES{"FAILED"}); } else { exit($EXIT_CODES{"SUCCESS"}); } } sub readLineNum($$) { my ($Path, $Num) = @_; return "" if(not $Path or not -f $Path); open (FILE, $Path); foreach (1 ... $Num) { ; } my $Line = ; close(FILE); return $Line; } sub cmd_find($$$$) { my ($Path, $Type, $Name, $MaxDepth) = @_; return () if(not $Path or not -e $Path); my $Cmd = "find \"$Path\""; if($MaxDepth) { $Cmd .= " -maxdepth $MaxDepth"; } if($Type) { $Cmd .= " -type $Type"; } if($Name) { if($Name=~/\]/) { $Cmd .= " -regex \"$Name\""; } else { $Cmd .= " -name \"$Name\""; } } return split(/\n/, `$Cmd`); } sub readDeps($$$) { my ($Path, $Dep, $RPMdep) = @_; my $Name = get_filename($Path); foreach my $Type ("provides", "suggests", "requires") { foreach my $D (split("\n", `rpm -qp -$Type $Path`)) { my ($N, $O, $V) = sepDep($D); $Dep->{$Type}{$N}{$O}{$V}=$Name; $RPMdep->{$Type}{$Name}{$N}=1; } } } sub staticCheck() { if(not $RPMdir and not $HDlist and not $RPMlist) { print STDERR gettext_("ERROR: --hdlist, --dir or --list option should be specified\n"); exit(1); } my (%Dep, %RPMdep, %AddedRPMs) = (); if($AddRPMs) { if(not -d $AddRPMs) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $AddRPMs); exit(1); } if(my @AddedRPMs = searchRPMs($AddRPMs)) { foreach my $Path (@AddedRPMs) { readDeps($Path, \%Dep, \%RPMdep); if(my $Name = get_RPMname($Path)) { $AddedRPMs{$Name}=1; } } } } if($RPMdir or $RPMlist) { print gettext_("Checking RPMs ...\n"); my @RPMs = (); if($RPMdir) { if(not -d $RPMdir) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $RPMdir); exit(1); } @RPMs = searchRPMs($RPMdir); } elsif($RPMlist) { @RPMs = readRPMlist($RPMlist, "RPMs"); } foreach my $Path (@RPMs) { if($AddRPMs) { if(my $Name = get_RPMname($Path)) { if($AddedRPMs{$Name}) { # already added next; } } } readDeps($Path, \%Dep, \%RPMdep); } } elsif($HDlist) { my $Content = readFile($HDlist); if($HDlist=~/(http|https|ftp):\/\//) { print gettext_("Downloading HDlist ...\n"); my $DownloadTo = $TMP_DIR."/extract/".get_filename($HDlist); $DownloadTo=~s/\.cz/\.gz/g; # cz == gz my $Dir = get_dirname($DownloadTo); mkdir($Dir); system("wget -U '' --no-check-certificate \"$HDlist\" --connect-timeout=5 --tries=1 --output-document=\"$DownloadTo\" >/dev/null 2>&1"); if(not -f $DownloadTo or not -s $DownloadTo) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $HDlist); exit(1); } my %Extract = ( "xz"=>"unxz", "lzma"=>"unlzma", "gz"=>"gunzip" ); if($DownloadTo=~/\.(gz|xz|lzma)\Z/) { my ($Format, $Cmd) = ($1, $Extract{$1}); if($Cmd) { system("cd $Dir && $Cmd $DownloadTo"); } my @Files = cmd_find($Dir, "f", "", ""); if(not @Files) { print STDERR gettext_("ERROR: cannot extract \'[_1]\'\n", $HDlist); exit(1); } $DownloadTo = $Files[0]; } if(my $Line = readLineNum($DownloadTo, 1)) { if($Line!~/\A\@\w+\@/) { print STDERR gettext_("ERROR: unknown format of hdlist\n"); exit(1); } } $Content = readFile($DownloadTo); } else { if(not -f $HDlist) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $HDlist); exit(1); } $Content = readFile($HDlist); } print gettext_("Checking HDlist ...\n"); my $Name = ""; foreach (reverse(split(/\n/, $Content))) { $_=~s/\A\@//g; my @Parts = split("\@", $_); my $Type = shift(@Parts); if($Type eq "info") { $Name = $Parts[0]; next; } if($AddRPMs) { if(my $PName = parse_RPMname($Name)) { if($AddedRPMs{$PName}) { # already added next; } } } if($Type=~/\A(requires|provides|suggests)\Z/) { foreach my $D (@Parts) { my ($N, $O, $V) = sepDep($D); $N=~s/\[\*\]//g;# /sbin/ldconfig[*] $Dep{$Type}{$N}{$O}{$V}=$Name; $RPMdep{$Type}{$Name}{$D} = 1; } } } } my %IgnoreDeps = (); if($FileDeps) { if(not -f $FileDeps) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $FileDeps); exit(1); } %IgnoreDeps = map {$_=>1} split(/\s+/, readFile($FileDeps)); } my (%Unresolved, %UnresolvedSuggested, %Broken) = (); foreach my $N (sort {lc($a) cmp lc($b)} keys(%{$Dep{"requires"}})) { foreach my $O (keys(%{$Dep{"requires"}{$N}})) { foreach my $V (keys(%{$Dep{"requires"}{$N}{$O}})) { if(not defined $Dep{"provides"}{$N} or not checkDeps($N, $O, $V, $Dep{"provides"}{$N})) { # unresolved if($N=~/\A(rpmlib|executable)\(.+\)\Z/) { # rpmlib(PayloadIsLzma), ... # executable(rm), ... next; } if($IgnoreDeps{$N}) { next; } my $Name = $Dep{"requires"}{$N}{$O}{$V}; if($RPMdep{"suggests"}{$Name}{$N}) { $UnresolvedSuggested{$N}{$O}{$V} = $Name; } else { $Unresolved{$N}{$O}{$V} = $Name; } $Broken{$Name}=1; } } } } my $Report = ""; if(my @Ns = sort {lc($a) cmp lc($b)} keys(%Unresolved)) { $Report .= "\n".gettext_("Unresolved \"Required\" Dependencies ([_1]):", $#Ns+1)."\n\n"; foreach my $N (@Ns) { foreach my $O (keys(%{$Unresolved{$N}})) { foreach my $V (keys(%{$Unresolved{$N}{$O}})) { $Report .= showDep($N, $O, $V)." (".gettext_("required by [_1]", $Unresolved{$N}{$O}{$V}).")\n"; } } } } if(my @Ns = sort {lc($a) cmp lc($b)} keys(%UnresolvedSuggested)) { if($Report) { $Report .= "\n"; } $Report .= "\n".gettext_("Unresolved \"Suggested\" Dependencies ([_1]):", $#Ns+1)."\n\n"; foreach my $N (@Ns) { foreach my $O (keys(%{$UnresolvedSuggested{$N}})) { foreach my $V (keys(%{$UnresolvedSuggested{$N}{$O}})) { $Report .= showDep($N, $O, $V)." (required by ".$UnresolvedSuggested{$N}{$O}{$V}.")\n"; } } } } if(my @Ns = sort {lc($a) cmp lc($b)} keys(%Broken)) { $Report .= "\n".gettext_("Broken Packages ([_1]):", $#Ns+1)."\n\n"; foreach my $N (@Ns) { $Report .= parse_RPMname($N)."\n"; } } if($Report) { print $Report."\n"; writeFile("$RESULTS_DIR/report.txt", $Report); } writeFile("$RESULTS_DIR/debug/rpm-provides.txt", Dumper($RPMdep{"provides"})); writeFile("$RESULTS_DIR/debug/rpm-requires.txt", Dumper($RPMdep{"requires"})); writeFile("$RESULTS_DIR/debug/rpm-suggests.txt", Dumper($RPMdep{"suggests"})); print gettext_("Report has been generated to:"); print "\n $RESULTS_DIR/report.txt\n"; if(keys(%Unresolved)) { exit($EXIT_CODES{"FAILED"}); } else { exit($EXIT_CODES{"SUCCESS"}); } } sub parse_RPMname($) { my $Name = $_[0]; if($Name=~/\d(mdv|mdk|rosa(\.\w+|))\d+/) { # plexus-interactivity-1.0-0.1.a5.2.2.5mdv2011.0.i586 $Name=~s/\-[^\-]+\Z//; $Name=~s/\-[^\-]+\Z//; } else { # x11-server-source-1.10.3-1-mdv2011.0.i586 $Name=~s/\-[^\-]+\Z//; $Name=~s/\-[^\-]+\Z//; $Name=~s/\-[^\-]+\Z//; } return $Name; } sub clearCache() { if(not $NoClean) { rmtree($RPM_CACHE); mkpath($RPM_CACHE); } } sub scenario() { if($Help) { HELP_MESSAGE(); exit(0); } if($ShowVersion) { print gettext_("URPM Repos Closure Checker [_1] for Mandriva Linux\nCopyright (C) 2012 ROSA Laboratory\nLicense: GPL \nThis program is free software: you can redistribute it and/or modify it.\n\nWritten by Andrey Ponomarenko.\n", $TOOL_VERSION); exit(0); } if($HDlist) { $StaticMode = 1; } if($Root) { if(not -d $Root) { print STDERR gettext_("ERROR: cannot access \'[_1]\'\n", $Root); exit(1); } } if($ResDir) { $RESULTS_DIR = $ResDir; } if(-d $RESULTS_DIR) { # print "Removing old $RESULTS_DIR directory\n"; rmtree($RESULTS_DIR); } if($CheckSignature) { if(not $ResDir) { $RESULTS_DIR .= "/signature"; } sigCheck(); exit(0); } if($StaticMode) { if(not $ResDir) { $RESULTS_DIR .= "/static"; } staticCheck(); } if($CheckRelease) { if(not $ResDir) { $RESULTS_DIR .= "/release"; } checkRelease(); exit(0); } if($DynamicMode) { if(not $ResDir) { $RESULTS_DIR .= "/dynamic"; } dynamicCheck(); } exit(0); } scenario();