#!/usr/bin/perl -w # # compare_golden.pl # # This script compares the local server with a "golden" depot -- a reference # depot that contains everything that should be installed on this server. # # # Olivier S. Masse, omasse .at. mayoxide .dot. com # Version 1.0 2007/04/28 # # # Copyright (c) 2007, Olivier S. Masse # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of Olivier S. Masse nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # use strict; my $verbose=0; my $ask=0; my $sync=0; my $golden=""; my $returnval=0; my $today; my $package; my $revision; my $stored_revision; my $vendor_revision; my @paddedver; my $description; my %localdepot_revisions; my %localdepot_vendor_revisions; my %golden_revisions; my %golden_vendor_revisions; my $goldenver; my $localver; my $comparator; my @updatelist; my $line; my $header=0; sub verbose { if ($verbose == 1) { print "@_"; } } sub header { if ($header == 0) { $header=1; printf ("%-36s%-3s%-35s\n", "Local", "?", "$golden"); printf ("%-36s%-3s%-35s\n", "-----------------------------------", "-", "-----------------------------------"); } } ############################################################################## # Main Program # ############################################################################## if (defined $ARGV[0]) { my $argnum; foreach $argnum (0 .. $#ARGV) { if ($ARGV[$argnum] eq "-v") { $verbose=1; } if ($ARGV[$argnum] eq "-ask") { $ask=1; } if ($ARGV[$argnum] eq "-sync") { $sync=1; } elsif ($ARGV[$argnum] ne "") { $golden=$ARGV[$argnum]; } } } if ($golden eq "") { print "Usage: compare_golden.pl [-v] [-sync] [-ask] golden_depot\n"; exit(1); } use POSIX qw(strftime); $today=strftime "%d/%m/%y %H:%M:%S %Z", localtime; print "======= $today BEGIN compare_golden.pl\n"; print " * Fetching contents of the depot...\n"; print " * Depot: $golden\n"; open (GOLDEN_SWLIST, "/usr/sbin/swlist -s $golden|") || die "unable to start swlist"; while () { if (($_ !~ /^$/) && ($_ !~ /^#/)) { chomp; # remove the CR s/^\s+//g; # remove leading whitespace s/\s+/:/; # replace the first chunk of remaining whitespace with a colon s/\s+/:/; # replace the rest of the whitespace with a colon, EXCEPT the last string which has a # description for which we keep whitespace ($package, $vendor_revision, $description) = split(/:/); # pad version numbers with zeroes. # Exeample: B.2.2.9.03 becomes 000B.0002.0002.0009.0003. # this makes it possible to compare weird version numbers to find out the latest foreach (split(/\./, "$vendor_revision")) { push(@paddedver, sprintf("%04s", $_)); } $revision=join('.', @paddedver); @paddedver=(); # If a package is more than once in the depot, we must only keep the latest revision if (defined $golden_revisions{$package}) { verbose "WARNING: detected duplicate of $package. "; $stored_revision = $golden_revisions{$package}; verbose "$revision gt $stored_revision ? "; if ($revision gt $stored_revision) { verbose "YES\n"; $golden_revisions{$package} = "$revision"; $golden_vendor_revisions{$package} = "$vendor_revision"; } else { verbose "NO\n"; } } else { $golden_vendor_revisions{$package} = "$vendor_revision"; $golden_revisions{$package} = "$revision"; } } } close (GOLDEN_SWLIST); verbose "\n"; print " * Comparing with installed packages on the server\n\n"; open (LOCAL_SWLIST, "/usr/sbin/swlist|") || die "not able to open swlist"; while () { if (($_ !~ /^$/) && ($_ !~ /^#/)) { chomp; # remove the CR s/^\s+//g; # remove leading whitespace s/\s+/:/; # replace the first chunk of remaining whitespace with a colon s/\s+/:/; # replace the rest of the whitespace with a colon, EXCEPT the last string which has a # description for which we keep whitespace ($package, $vendor_revision, $description) = split(/:/); # pad version numbers with zeroes. # Exeample: B.2.2.9.03 becomes 000B.0002.0002.0009.0003. # this makes it possible to compare weird version numbers to find out the latest foreach (split(/\./, "$vendor_revision")) { push(@paddedver, sprintf("%04s", $_)); } $revision=join('.', @paddedver); @paddedver=(); $localdepot_revisions{$package} = "$revision"; $localdepot_vendor_revisions{$package} = "$vendor_revision" } } verbose "\n"; foreach $package (keys %golden_revisions) { $goldenver=$golden_revisions{$package}; if (defined $localdepot_revisions{$package}) { $localver=$localdepot_revisions{$package}; if ($localver eq $goldenver) { $comparator="="; } if ($localver lt $goldenver) { $comparator="<"; } if ($localver gt $goldenver) { $comparator=">"; } if ($comparator ne "=") { header; printf ("%-36s%-3s%-35s\n", "$package,r=$localdepot_vendor_revisions{$package}", "$comparator", "$package,r=$golden_vendor_revisions{$package}"); push(@updatelist,$package); $returnval=1; } elsif ($verbose == "1") { header; printf ("%-36s%-3s%-35s\n", "$package,r=$localdepot_vendor_revisions{$package}", "$comparator", "$package,r=$golden_vendor_revisions{$package}"); } } else { header; printf ("%-36s%-3s%-35s\n","(missing)", "X", "$package,r=$golden_vendor_revisions{$package}"); push(@updatelist,$package); $returnval=1; } } print "\n"; if ($returnval == 0) { print "NOTE: This server is already up to date with the depot.\n"; } else { print "NOTE: This server is not up to date with the depot.\n"; if ($sync == 1) { foreach $package (@updatelist) { if ($ask == 1) { print "\n"; print " Q: Update or install package '$package' [Y/n] ? "; $line=; if ($line =~ /^y$/ || $line =~ /^Y$/ || $line =~ /^$/) { print " > /usr/sbin/swinstall -s $golden $package\n"; if (system("/usr/sbin/swinstall -s $golden $package\n") != 0) { print "\n"; print "ERROR: There was a problem while installing package '$package'.\n"; print " If a reboot is required, you will have to install it manally.\n"; $returnval=2; } else { print " * Package '$package' installed succesfully\n"; } } else { print "'$package' installation skipped.\n"; } } else { print " * Installing package '$package'\n"; print " > /usr/sbin/swinstall -s $golden $package\n"; if (system("/usr/sbin/swinstall -s $golden $package\n") != 0) { print "\n"; print "ERROR: There was a problem while installing package '$package'.\n"; print " If a reboot is required, you will have to install it manally.\n"; $returnval=2; } else { print " * Package '$package' installed succesfully\n"; } } } if ($returnval == 0) { print " * Installation of all packages succesful.\n\n"; } if ($returnval == 2) { print "ERROR: There has been at least one package that did not install properly.\n\n"; } } } use POSIX qw(strftime); $today=strftime "%d/%m/%y %H:%M:%S %Z", localtime; print "\n\n======= $today END compare_golden.pl\n"; exit($returnval); # EOF