#!/usr/local/bin/perl # # This script analyzes a Sun patch report (e.g. a Solaris2.x.PatchReport # file off of ftp://sunsolve.sun.com/pub/patches), compares it against the # list of currently installed patches, and generates a listing of patches # which need to be installed or updated. # # -- JJC, 10/3/96 # require "getopts.pl"; $patchdir = '/var/sadm/patch'; $patchpattern = '\d{6,6}-\d{2,2}'; $verspattern = '-\d{2,2}'; ($script) = $0 =~ /([^\/]*)$/; # Get script name from invocation path. &Getopts("p:"); die "Usage: $script [-p patchlistsource] patchreport_file\n" if (@ARGV != 1); $patchreport = $ARGV[0]; &parse_report($patchreport); if (!$opt_p) { $patchlistsource = "ls $patchdir |"; } else { $patchlistsource = ($opt_p =~ /\s/ ? "$opt_p |" : $opt_p); } open(PATCHLIST, $patchlistsource) || die "$script: can't get patch list from '$patchlistsource'\n"; @ourpatches = &justlatestpatches(grep(chomp, )); close(PATCHLIST); foreach $patch (@ourpatches) { ($justpatch, $vers) = $patch =~ /([^-]+)(.*)/; $ourpatchvers{$justpatch} = $vers; } @ourobsolete = &intersect(*ourpatches, *obsoletedby, 0); @haverec = &intersect(*recommended, *ourpatches, 1); @otherrec = &difference(*recommended, *haverec, 1); @updaterec = &addversions(&intersect(*otherrec, *ourpatches, 0)); @missingrec = &difference(*otherrec, *updaterec, 1); @havesec = &intersect(*security, *ourpatches, 1); @othersec = &difference(*security, *havesec, 1); @updatesec = &addversions(&intersect(*othersec, *ourpatches, 0)); @missingsec = &difference(*othersec, *updatesec, 1); @haveall = &intersect(*allpatches, *ourpatches, 1); @otherall = &difference(*allpatches, *haveall, 1); @updateall = &addversions(&intersect(*otherall, *ourpatches, 0)); @masterlist = &union(*updateall, *missingrec, 1); @masterlist = &union(*masterlist, *missingsec, 1); @shoppinglist = &difference(*otherall, *masterlist, 1); $boxline = $report_title; $boxline =~ s/./\*/g; print "$boxline\n$report_title\n$boxline\n\n\n"; if (@ourobsolete) { print <", "\n"; } print "\nTotal patches listed: ", scalar(@plist), "\n" if (@plist > 1); } sub intersect { local(*plist1, *plist2, $keepversion) = @_; local(@p1) = defined(@plist1) ? @plist1 : keys %plist1; local(@p2) = defined(@plist2) ? @plist2 : keys %plist2; local(%temp); $keepversion || grep(s/$verspattern$//, @p1); $keepversion || grep(s/$verspattern$//, @p2); grep($temp{$_}++, @p1); return sort(grep($temp{$_}, @p2)); } sub difference { local(*plist1, *plist2, $keepversion) = @_; local(@p1) = defined(@plist1) ? @plist1 : keys %plist1; local(@p2) = defined(@plist2) ? @plist2 : keys %plist2; local(%temp); $keepversion || grep(s/$verspattern$//, @p1); $keepversion || grep(s/$verspattern$//, @p2); grep($temp{$_}++, @p2); return sort(grep(!$temp{$_}, @p1)); } sub union { local(*plist1, *plist2, $keepversion) = @_; local(@p1) = defined(@plist1) ? @plist1 : keys %plist1; local(@p2) = defined(@plist2) ? @plist2 : keys %plist2; $keepversion || grep(s/$verspattern$//, @p1); $keepversion || grep(s/$verspattern$//, @p2); return &justlatestpatches(@p1, @p2); } sub justlatestpatches { local(@plist) = @_; local($justpatch, $patch, @result, %temppatch, $vers); foreach $patch (sort @plist) { ($justpatch, $vers) = $patch =~/([^-]+)(.*)/; $temppatch{$justpatch} = $vers || ""; } foreach $patch (sort keys %temppatch) { push(@result, "$patch$temppatch{$patch}"); } return @result; } sub addversions { local(@plist) = @_; local($patch, @result); foreach $patch (@plist) { push(@result, "$patch".($patchvers{$patch} || "-??")); } return @result; } sub parse_report { local($patchreport) = @_; local($description, $justpatch, $newpatchID, $patchID, $vers, $x); open(PATCHREPORT, $patchreport) || die "$script: unable to open $patchreport\n"; lookfor(PATCHREPORT, '/\s*Title/'); $report_title = $_.; $report_title =~ s/\s+/ /g; lookfor(PATCHREPORT, '/Recommended Patches:\s*$/'); lookfor(PATCHREPORT, "/^$patchpattern/"); while () { last unless /^$patchpattern/; ($patchID, $description) = /^($patchpattern)\s+(.*)\n/; $recommended{$patchID} = $description; $_ = ; } lookfor(PATCHREPORT, '/Security Fixes:\s*$/'); lookfor(PATCHREPORT, "/^$patchpattern/"); while () { last unless /^$patchpattern/; ($patchID, $description) = /^($patchpattern)\s+(.*)\n/; $security{$patchID} = $description; $_ = ; } lookfor(PATCHREPORT, '/Obsoleted Patches:\s*$/'); lookfor(PATCHREPORT, "/^$patchpattern/"); while () { last unless /^$patchpattern/; ($patchID, $newpatchID) = /^($patchpattern)\s+OBSOLETED by\s+(\d+)/; ($justpatch, $vers) = $patchID =~ /([^-]+)(.*)/; $obsoletedby{$justpatch} = $newpatchID; $_ = ; } lookfor(PATCHREPORT, '/Complete Listing of Released Patches:\s*$/'); while () { lookfor(PATCHREPORT, '/^Patch-ID#\s+'.$patchpattern.'\s*$/'); last if eof(PATCHREPORT); ($patchID) = /($patchpattern)/; chomp($_ = ); ($x, $description) = split(/\s+/, $_, 2); $allpatches{$patchID} = $description; ($justpatch, $vers) = $patchID =~ /([^-]+)(.*)/; $patchvers{$justpatch} = $vers; } close PATCHREPORT; } sub lookfor { local($filehandle, $regex) = @_; while (<$filehandle>) { last if eval $regex; } }