Listing 1 compare_patches.pl
#!/usr/bin/perl
#
# Compare patches installed across Sun servers
# NEPD Consulting
# Paul Guglielmino <paulg@nepd.com>
#
# Please send improvements or comments to me!
#
#
# 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; either version 2 of the License, or
# any later version.
#
# 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.
#
use Getopt::Long;
my($version) = "1";
my($want_only_diffs, $showrev, $patchk, $debug, $usage, $v, $noheader);
Getopt::Long::Configure( 'permute' );
GetOptions( 'diffs' => \$want_only_diffs,
'showrev' => \$showrev, 'patchk' => \$patchk,
'debug' => \$debug, 'help' => \$usage,
'version' => \$v, 'noheader' => \$noheader );
if ( $usage ) { print_usage(); exit 0; }
if ( $v ) { print_version(); exit 0; }
#####
my(@server_files, @server_patch_levels) = ();
my(%server_patch_levels) = ();
foreach my $datafile (@ARGV) {
if ( ! -f $datafile ) {
print "File $datafile not available, skipping\n";
} else {
push( @server_files, $datafile );
}
}
if ( $#server_files < 1 ) {
print "Sorry need at least two files to analyze.\n";
print_usage();
exit 2;
}
# Assume showrev output unless told otherwise
if ( $patchk ) {
$sub_ref = \&parse_patchk_file;
} else {
$sub_ref = \&parse_showrev_file;
}
# Load each file into memory
for my $i (0..$#server_files) {
$debug && print "Parsing $server_files[$i] ...\n";
open(F, "$server_files[$i]");
&$sub_ref(F,$i);
close(F);
}
if ( ! $noheader ) {
print_header();
}
my($patchdiffs) = 0;
my(%count) = ();
# Determine all patch ids in use by iterating over all servers and patches
foreach my $s (keys %server_patch_levels) {
foreach my $p (keys %{$server_patch_levels{$s}}) {
$count{$p} = 1;
}
}
my($numpatches) = scalar keys %count;
my(@all_patches) = keys %count;
# Key off the patch id to see what level each server is at
foreach my $p (@all_patches) {
my($linestring, $diff_string) = "";
my($diff_patch_flag) = 0;
for my $i (0..$#server_files) {
$linestring .= sprintf " %s: %3s |", $server_files[$i], \
$server_patch_levels{$i}{$p} || "N/A";
}
for my $j (0..$#server_files-1) {
if ( $server_patch_levels{$j}{$p} != $server_patch_levels{$j+1}{$p} ) {
$diff_patch_flag = 1;
}
}
if ( $diff_patch_flag ) {
$patchdiffs++;
$diff_string .= "*";
}
if ( (! defined $want_only_diffs) ||
($diff_patch_flag && $want_only_diffs) ) {
printf "%-6s%-2s || %s\n", "$p", $diff_string, $linestring;
}
}
if ( ! $noheader ) {
print "\nDifferent patches: $patchdiffs out of total $numpatches\n";
}
exit 0;
# Load patch information from "showrev -p" file
sub parse_showrev_file {
my($fh,$i) = @_;
while( <$fh> ) {
# Match only the lines that have installed patches
if ( /^Patch: (\d{6})-(\d{2})/ ) {
$debug && print "Matched patch: $1 - $2\n";
# Load the patch and revision number into our hash if it:
# Is not already there or if it is there with a lower
# revision number
if ( (! defined $server_patch_levels{$i}{$1}) ||
($server_patch_levels{$i}{$1} < $2) ) {
$server_patch_levels{$i}{$1} = $2;
}
}
}
}
# Load patch information from Sun "patchk.pl" file
sub parse_patchk_file {
my($fh,$i) = @_;
while( <$fh> ) {
if ( /^(\d{6}) (\d{2})/ ) {
$debug && print "Matched patch: $1 - $2\n";
# Load the patch and revision number into our hash if it:
# Is not already there or if it is there with a lower revision
# number
if ( (! defined $server_patch_levels{$i}{$1}) ||
($server_patch_levels{$i}{$1} < $2) ) {
$server_patch_levels{$i}{$1} = $2;
}
}
}
}
# Print results header to the screen
sub print_header {
$line = "Patch ID || ";
foreach $s (@server_files) {
$len = length($s) - 1;
$line .= "Rev #" . " "x$len . " | ";
}
print "$line\n";
}
sub print_usage {
print<<EOT;
Show differences between patch levels on Solaris servers
Usage: $0 [OPTIONS]... [FILES]...
\t --help [This message]
\t --debug [Show debugging output]
\t --diff [Show only differences]
\t --noheader [Print header (default)]
\t --showrev [Input files are in showrev -p format (default)]
\t --patchk [Input files are in Sun patchk.pl format]
\t --version [Print version number]
EOT
}
sub print_version {
print "\n$0: Version $version\n\n";
}
|