---------- X-Sun-Data-Type: text X-Sun-Data-Description: text X-Sun-Data-Name: text X-Sun-Charset: us-ascii X-Sun-Content-Lines: 38 Question: > Some time ago one of you sent me a script which checks installed > patches against a list of actual patches (from file or by ftp from SUN) > and gives the differences (or ftps the new patches from SUN) and so on... Answers by (in chronological order and without flames... :-): Tom Mornini tmornini@sun630mp.infomania.com Richard Skelton rich@brake.demon.co.uk Kevin Davidson tkld@cogsci.ed.ac.uk Charlie Mengler charliem@anchorchips.com Celeste Stokely celeste@celestial.stokely.com Louis Avrami L.Avrami@dialogic.com Susan Feng sfeng@smi.Stanford.EDU John D Groenveld groenvel@cse.psu.edu Rick Reineman reineman1@llnl.gov Systems Admin sysadmin@lvision.com Answers: - look at SUNSOLVE (CD or http://sunsolve1.sun.com) - attached patchcheck - attached patchreporter - get the info from SUN: "showrev -a | mail patch@East.Sun.COM" - PatchReport or PatchDiag, see http://www.stokely.com/unix.sysadm.resources/faqs.s.html#solaris.patch.mgmt (and don't forget to see the rest of their information!!!) - PatchReport at ftp://x86.cs.duke.edu/pub/PatchReport/ - patchdiag at http://sunsolve.sun.com/sunsolve/whats-new.html Bye, Chris ---------- X-Sun-Data-Type: octet-stream X-Sun-Data-Name: patchcheck X-Sun-Charset: us-ascii X-Sun-Content-Lines: 351 #!/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; } } ---------- X-Sun-Data-Type: text X-Sun-Data-Name: patchreporter X-Sun-Charset: us-ascii X-Sun-Content-Lines: 150 #!/usr/local/bin/perl 'di'; 'ig00'; # patchreporter -- Kevin Davidson # # $Log: patchreporter,v $ # Revision 1.1 1997/07/17 10:52:22 tkld # Initial revision # $RCSHEADER = '$Header: /home/tkld/src/sunpatches/RCS/patchreporter,v 1.1 1997/07/17 10:52:22 tkld Exp $'; #' '$Revision: 1.1 $' =~ /^\$\w+:\s+([.1234567890]+)\s+\$$/; #' $VERSION = $1; require URI::URL; use LWP::Simple; $patchreporturl="ftp://online.sunsolve.sun.co.uk/pub/patches/Solaris2.5.1.PatchReport"; $patchdir="/usr/local/install/jumpstart/patches"; $section="head"; if ($#ARGV == 0) { $patchreporturl=$ARGV[0]; } # Fetch latest patch report $url = new URI::URL $patchreporturl; if (!($patchtext=get($url))) { die "Cannot get patch report from $patchreporturl\n"; } # Assemble list of currently available patches opendir(P1,$patchdir) || die "$patchdir: $!\n"; foreach $dir (grep { /^\d+/} readdir(P1)) { opendir (P2,"$patchdir/$dir") || die "$patchdir/$dir: $!\n"; foreach $ddir (grep { /^\d+/} readdir(P2)) { $ddir =~ /^(\d+)-(\d+)$/; $patches{$1}=$2; # Save rev of current patch } closedir(P2); } closedir(P1); $got=0; foreach $_ (split(/\n/,$patchtext)) { $section="new" if /New Patches Released Since/; $section="recommended" if /Recommended Patches/; $section="security" if /Patches Containing Security Fixes/; $section="obsolete" if /Obsoleted Patches/; $section="fulllist" if /Complete Listing of Released/; if ($section ne "fullist") { next unless /^\s*(\d\d\d\d\d\d)-(\d\d)\s*(.*)$/; $patch=$1; $rev=$2; $description=$3; ®ister($section,$patch,$rev,$description); } else { if (/^Patch-ID.*(\d+)-(\d+)/) { $patch=$1; $rev=$2; $got=1; } if ($got && /^Synopsis:\s*(.*)$/) { $got=0; $description=$1; ®ister($section,$patch,$rev,$description); } } # print "$section: $patch (rev $rev): $description\n"; } print "Comparing patches available in $patchdir\nwith current patch report\n"; print "$patchreporturl\n\n"; print "New patches to consider:\n"; foreach $p (sort keys %consider) { print "$p-$consider{$p}\t$descriptions{$p}\n"; } print "\nMissing recommended patches:\n"; foreach $p (sort keys %new) { print "$p-$new{$p}\t$descriptions{$p}\n"; } print "\nUpdated revs of existing patches:\n"; foreach $p (sort keys %updated) { print "$p-$updated{$p} ($patches{$p})\t$descriptions{$p}\n"; } print "\nObsolete patches:\n"; foreach $p (sort keys %obsolete) { print "$p-$obsolete{$p}\t$descriptions{$p}\n"; } sub register { my($section,$patch,$rev,$description) = @_; $descriptions{$patch}=$description; if (!($section eq "obsolete") && $patches{$patch} && $patches{$patch} < $rev) { $updated{$patch} = $rev; } if ($section eq "obsolete" && $patches{$patch} && $patches{$patch} <= $rev) { $obsolete{$patch}=$patches{$patch}; } if (($section eq "recommended" || $section eq "security") && !$patches{$patch} ) { $new{$patch}=$rev; } if ($section eq "new" && !$patches{$patch}) { $consider{$patch}=$rev; } } ######################################################## # These next few lines are legal in both Perl and Nroff. .00; # finish .ig 'di \" finish diversion--previous line must be blank .nr nl 0-1 \" fake up transition to first page again .nr % 0 \" start at page 1 '; __END__ #### From here on it's a standard manual page #### .TH PATCHREPORTER 8 "Thu Jun 5 1997" "Centre for Cognitive Science, University of Edinburgh" .SH NAME patchreporter \- Check for latest Sun patches .SH SYNOPSIS .B patchreporter [url] .SH DESCRIPTION Fetches the latest Solaris patch report, from the specified URL if given, or from a built in default and compares that patch list with the versions of patches available in the JumpStart patch directory. A report is created of newly released patches to consider, recommended or security patches that are missing and updated revisions of existing patches. Also any patches in the patch directory that are no obsolete are flagged and should be removed when the replacement patch is fetched. .SH FILES .TP /usr/local/install/jumpstart/patches Patch directory. Beneath this are directories named `01', `02', etc. containing patches to be installed in numerical order. Typically `01' will contain patches that are prerequisites for other patches to ensure they are installed first, `02' will contain other system patches, `03' contains OpenWindows patches and `04' CDE patches. .SH AUTHOR Kevin Davidson tkld@cogsci.ed.ac.uk .SH SEE ALSO JumpStart -- ("`-''-/").___..--''"`-._ Christian.Masopust@siemens.at `o_ o ) `-. ( ).`-.__.`) System Manager Phone: +43-1-1707-24516 (_Y_.)' ._ ) `._ `. ``-..-' Siemens AG Austria Fax: +43-1-1707-53759 _..`--'_..-_/ /--'_.' .' Siemensstr. 88-92 (il).-'' (li).' ((!.-' A-1210 Wien, Austria