#!/usr/bin/perl -w ################################################################################ # mkpcadir: a helper tool to create a patchdiag.xref and patch download # directory from a Sun Solaris EIS patch set DVD, so that we can use Martin # Paul's Patch Check Advanced (pca) to distribute and apply Sun EIS patch sets. # # usage: mkpcadir [ -q ] [ -t ] # # # -q be quiet, but not silent # -t directory in which to unpack compressed sets, defaults to /tmp # # mkpcadir is designed to flatten the directory hierarchy usually found on Sun # Solaris EIS patch DVDs, which is assumed to be mounted at . # In the process, mkpcadir also filters the file supplied so # that the resulting xref file only references patches that are found on the # DVD. The resulting xref file, along with all of the patches found under # are copied to , which may be NFS # mounted or served over FTP or HTTP according to preference, to Solaris # clients running pca. # # mkpcadir requires scratch space in which to unpack various *_Recommended # patch sets, and then individually compress the patches found inside. # Currently, the required space exceeds 1GiB. The default of /tmp may not be # suitable on smaller servers. # # When not invoked in quiet mode, mkpcadir may complain about differing # duplicate files. Duplicate filenames at different paths are assumed to be # the same file, to resolve the many duplicates found on the EIS DVDs. Files # like "README" are NOT likely to be the same and you can safely ignore them if # you see them in the list of differing files that mkpcadir produces. Files # like 123456-78.zip ARE likely to be the same, and should not appear in the # list of differing files. Please check that the list does not include any # file that looks like a patch. # # More information on the operation of pca can be found at: # # http://www.par.univie.ac.at/solaris/pca/ # # Example: Install May 2007 EIS DVD in /export/patches/htdocs/2007-05 # # mkpcadir /cdrom/patchdvd-22may07/sun/patch/etc/patchdiag.xref \ # /cdrom/patchdvd-22may07/sun/patch /export/patches/htdocs/2007-05 # # The result will be a 2007-05 directory containing some 2.1GiB of patches and # a single patchdiag.xref file. If this directory is published via HTTP, for # example, pca might then be called like so: # # pca --xrefurl=http://webserver/patches/2007-05/ \ # --patchurl=http://webserver/patches/2007-05/ missingrs # # You are free to use, modify or distribute this program under the terms of the # GNU General Public License: # # http://www.gnu.org/copyleft/gpl.html # # Version: $Id: mkpcadir.pl,v 1.7 2007/09/05 09:45:07 car Exp $ # Author: Chris Reece use strict; my $me = `basename $0`; chomp $me; my $usage = "usage: $me [ -q ] [ -t ] "; $#ARGV >= 2 || $#ARGV <= 5 || die "$usage\n"; my $quiet = 0; my $tmp = '/tmp'; while ($#ARGV > 2) { if ($ARGV[0] eq '-q') { shift; $quiet = 1; } elsif ($ARGV[0] eq '-t') { shift; $tmp = shift; } else { die "$usage\n" unless $ARGV[0] eq "-q"; } } $#ARGV == 2 || die "$usage\n"; my $destinationDirectory = $ARGV[2]; my $sourceDirectory = $ARGV[1]; my $sourceXref = $ARGV[0]; ################################################################################ # globals my $destinationXref = $destinationDirectory . '/patchdiag.xref'; my $tmpDirectory = $tmp . '/.mkpcadir.' . "$$"; my %filePaths; my @patchPaths; ################################################################################ # functions sub PopulateFilePaths($;$); sub PopulateFilePaths($;$) { my ($filePath, $base) = @_; if(!defined($base)) { $base = $filePath; } if (-d $filePath) { opendir DH, "$filePath" || die "$me: could not open directory: $filePath\n"; foreach my $child (readdir DH) { if ($child !~ m/^\.{1,2}$/) { PopulateFilePaths($filePath . '/' . $child, $child); } } closedir DH; } elsif (-f $filePath && $base !~ m/README|\.name|info\.txt|\.pre-unpack|install_all_patches|install_cluster_fast|patch_order|\.post-unpack|manual\.txt|install_cluster|copyright/) { if(defined($filePaths{$base})) { `cmp -s $filePaths{$base} $filePath`; if($?>>8) { warn "$me: files differ:\n$filePaths{$base}\n$filePath\n" unless $quiet; } } $filePaths{$base} = $filePath; } } sub FilterXref() { open(SOURCEXREF, "<$sourceXref") || die "$me: can't open $sourceXref\n"; open(DESTINATIONXREF, ">$destinationXref") || die "$me: can't open $destinationXref\n"; while() { if (m/^\s*#/ || m/^\s*$/) { print DESTINATIONXREF $_; next; } my ($patch, $revision, @excess) = split /\|/, $_; if (defined($revision) && length $revision > 0) { $patch = "$patch-$revision"; } foreach my $fileName (keys %filePaths) { if ($fileName =~ m/^$patch/) { print DESTINATIONXREF $_; push @patchPaths, $filePaths{$fileName}; } } } close DESTINATIONXREF; close SOURCEXREF; } sub CopyPatches() { foreach my $patchPath (@patchPaths) { print '.' unless $quiet; `cp $patchPath $destinationDirectory`; } } sub TranslateUnpackedPatch($$) { my ($setPath, $patch) = @_; print '.' unless $quiet; `cd $setPath && zip -r $patch $patch && rm -rf $patch`; if($?>>8) { warn "$me: at: $setPath/$patch\n"; } } sub TranslateUnpackedPatches($) { my ($setPath) = @_; opendir SDH, "$setPath" || die "$me: could not open directory: $setPath\n"; foreach my $child (readdir SDH) { if (-d "$setPath/$child" && $child !~ m/^\.{1,2}$/) { TranslateUnpackedPatch($setPath, $child); } } closedir SDH; } sub TranslateUnpackedPatchSets() { opendir DH, "$tmpDirectory" || die "$me: could not open directory: $tmpDirectory\n"; foreach my $child (readdir DH) { if ($child =~ m/Recommended$/) { TranslateUnpackedPatches($tmpDirectory . '/' . $child); } } closedir DH; } sub UnpackRecommended($$) { my ($fileName, $extensions) = @_; print "Unpacking $fileName\n" unless $quiet; my $filePath = $filePaths{$fileName}; if ($extensions eq 'README') { return; } elsif ($extensions eq 'zip') { `unzip -d $tmpDirectory $filePath`; } elsif ($extensions eq 'tar.bz2') { `bzcat $filePath | ( cd $tmpDirectory && tar xf - )`; } else { warn "$me: unknown set extension: $fileName\n"; } } sub UnpackRecommendeds() { foreach my $fileName (keys %filePaths) { if (my ($extensions) = ($fileName =~ m/Recommended\.(\S+)$/)) { UnpackRecommended($fileName, $extensions); } } } sub CleanUp() { print "Removing $tmpDirectory\n" unless $quiet; `rm -rf $tmpDirectory`; } ################################################################################ # main -r $sourceXref || die "$me: can't read xref file: $sourceXref\n"; -w $destinationDirectory || die "$me: not writeable: $destinationDirectory\n"; -d $destinationDirectory || die "$me: not a directory: $destinationDirectory\n"; print "Creating $tmpDirectory\n" unless $quiet; mkdir $tmpDirectory || warn "$me: can't create directory: $tmpDirectory\n"; -w $tmpDirectory || die "$me: not writeable: $tmpDirectory\n"; -d $tmpDirectory || die "$me: not a directory: $tmpDirectory\n"; print "Finding patches\n"; PopulateFilePaths($sourceDirectory); print "Unpacking recommended sets\n"; UnpackRecommendeds(); print "Translating unpacked sets"; TranslateUnpackedPatchSets(); print "\n"; print "Importing recommended sets\n"; PopulateFilePaths($tmpDirectory); print "Filtering Xref\n"; FilterXref(); print "Copying patches"; CopyPatches(); print "\n"; print "Cleaning up\n"; CleanUp();