#!/bin/sh -- # Really perl eval 'exec perl $0 ${1+"$@"}' if 0; # PCA - Patch Check Advanced 1.0 # Release Date: 2003/09/09 # # Author: Martin Paul # Home : http://www.par.univie.ac.at/~martin/pca/ # # PCA is a replacement for Sun's Patch Check tool. It shows lists of # installed and uninstalled patches. With the help of PCA the system # administrator can easily find out which patches should/need to be # applied to a system. use Getopt::Std; # Check for command line options # $show_installed=0; $show_uninstalled=0; $show_uninstalled_rsonly=0; $show_header=1; &getopts("iurah") || &usage && exit 1; if ($opt_i || $opt_a) { $show_installed=1; } if ($opt_u || $opt_a) { $show_uninstalled=1; } if ($opt_r) { $show_uninstalled=1; $show_uninstalled_rsonly=1; } if ($opt_h) { $show_header=0; } # Set default option if ($show_installed == 0 && $show_uninstalled == 0) { $show_uninstalled=1; $show_uninstalled_rsonly=1; } # Get patchdiag.xref location. # $xrefdir=@ENV{"XREF"}; if ($xrefdir eq "") { $xrefdir = $0; if ($xrefdir !~ /\//) { $xrefdir = '.'; } else { $xrefdir =~ s/\/[^\/]*$//; } } $XREF="$xrefdir/patchdiag.xref"; # Get architecture # open(UNAME, "/usr/bin/uname -p |"); $arch = ; chop ($arch); close UNAME; # Read patchdiag.xref # open(XREF, "<$XREF") || die "Can't find xref file $XREF"; $/=""; $xref = ; $/="\n"; close XREF; @xref = split( /\n/, $xref ); # Read showrev -p output. # open(SHOWREV, "/usr/bin/showrev -p | sort |"); while () { if ($_ =~ m/^Patch:\s+(\d{6})-(\d{2}).*/) { $installed_patches{$1} = $2; } else { die "Can't parse showrev -p output"; } } close SHOWREV; $first_header=1; if ($show_installed == 1) { print_header ("INSTALLED"); foreach $i (sort(keys(%installed_patches))) { $irev = $installed_patches{$i}; # This is needed because sometimes a patch id is listed multiple times # in patchdiag.xref (old revisions are only marked O, not removed). @ret=grep(/^$i\|/, @xref ); @return = reverse sort @ret; if (@return == "") { $id=$i; $rFlag=""; $sFlag=""; $crev="--"; $synopsis = "NOT FOUND IN CROSS REFERENCE FILE!" } else { ($id, $crev, $reldate, $rFlag, $sFlag, $oFlag, $byFlag, $os, $archs, $pkgs, $synopsis ) = split( /\|/, $return[0]); } print_patch ($id, $irev, $crev, $rFlag, $sFlag, $synopsis); } } if ($show_uninstalled == 1) { if ($show_uninstalled_rsonly == 1) { print_header ("UNINSTALLED RECOMMENDED/SECURITY"); } else { print_header ("UNINSTALLED"); } # Read pkginfo open( PKGINFO, "/usr/bin/pkginfo -l|" ); $/="\0x07"; $pkginfo = ; $/="\n"; close( PKGINFO ); # massaging text to make searches & split easier $pkginfo =~ s/\n\n/#/g; $pkginfo =~ s/\n/|/g; $pkginfo =~ s/\s*[A-Z]+:\s*//g; @pkginfo = split(/#/, $pkginfo); foreach $i (@pkginfo) { ($package, $name, $category, $archs, $version, $basedir, $vendor, $desc, $pstamp, $instdate, $hotline, $status, $files ) = split(/\|/, $i); $pkgidx = $package.":".$version; $instpkgs{$pkgidx}=1; } # Sometimes multiple revisions of one patch are in patchdiag.xref. # We don't really take care of that as of now, but usually the older # revisions are marked O or B, so it's not a problem. # foreach $i (sort @xref) { ($id, $crev, $reldate, $rFlag, $sFlag, $oFlag, $byFlag, $os, $archs, $pkgs, $synopsis ) = split( /\|/, $i); # Ignore obsolete and bad patches if ($oFlag eq "O") { next;} if ($byFlag =~ ".B") { next;} # Ignore patches which have been shown already if ($shown{$id} == 1) { next;} # Ignore patches which are installed in their current revision # Sometimes installed patch revisions are higher than patchdiag.xref, # therefore we use ">=" instead of "==". $irev = $installed_patches{$id}; if ($irev >= $crev) { next; } # Ignore patches without R/S flag, if the user wants that. if ($show_uninstalled_rsonly == 1 && $rFlag ne 'R' && $sFlag ne 'S') { next; } # Check if patch is for our architecture. If not, ignore it. # The architecture naming in the xref file isn't perfect, we have to use =~ @archlist= split (/\;/, $archs); $found=0; foreach $j (@archlist) { if (($j =~ $arch) || ($j =~ "all")) { $found=1; } } if ($found == 0) { next; } # Check if patch is for a package we have installed. If not, ignore it. @pkglist= split (/\;/, $pkgs); $found=0; foreach $j (@pkglist) { if ($instpkgs{$j} == 1) { $found=1; } } if ($found == 0) { next; } # After all checks, print the patch and mark it as shown. if ($irev eq "") { $irev="--"; } print_patch ($id, $irev, $crev, $rFlag, $sFlag, $synopsis); $shown{$id}=1; } } sub print_patch { # Use %-.62s for synopsis if you want to limit it so that lines won't wrap. if ($irev < $crev) { $i = "<"; } if ($irev == $crev) { $i = "="; } if ($irev > $crev) { $i = ">"; } printf "%6d %2s %1s %2s %1s%1s %s\n", $_[0], $_[1], $i, $_[2], $_[3], $_[4], $_[5]; } sub print_header { if ($show_header == 1) { if ($first_header == 1) { $first_header=0; } else { print "\n\n"; } print "$_[0] PATCHES\n"; print "Patch IR CR RS Synopsis\n"; print "------ -- - -- -- ------------------------------------------------------------\n"; } } sub usage { print "Usage: $0 [-iurah]\n"; print " -i Show installed patches\n"; print " -u Show uninstalled patches\n"; print " -r Show uninstalled recommended/security patches only (default)\n"; print " -a Show all patches (combines -iu)\n"; print " -h Don't show descriptive headers\n"; }