Re: Checking for most recent Solaris Security Patches

From: Paul Brunk (pbrunkat_private)
Date: Fri Jan 08 1999 - 06:56:21 PST

  • Next message: Deborah A. Greenberg: "NFR Version 2.0.2 Research Now Available"

    On Thu, 7 Jan 1999, Ronan Waide wrote:
    
    > On January 6, spamhaterat_private said:
    > > Enclosed is a script that checks if your Solaris system has the
    > > latest security patches applied.
    
    > Funnily enough... :)
    >
    > I've a version of a similar program sitting on one of the Solaris
    > boxen here for the last few months[...]
    
    Even more hilarious:
    
    W. Joseph Shamblin posted somewhere an excellent program for this about a
    year ago.  Uses perl, many options.  Uses either "patchdiag.xref or"
    "Solaris2.x.PatchReport".
    
    --
    Tired of the pretence,
    Paul Brunk, Workstation Support Droid
    "Hungry like the wolf"
    
    #!/usr/local/bin/perl
    # This program is intended to parse the patchdiag.xref file downloaded from
    # the sunsolve ftp site. It parses the file, and returns a list of patches that
    # need to be evalutated for installation.
    # @(#) PatchReport 2.8@(#) (Shamblin) 01/24/98 21:54:04
    
    # Copyright (c) 1997 by W. Joseph Shamblin.  All rights reserved.
    # Permission is granted to reproduce and distribute this program
    # with the following restrictions:
    #   1) This copyright notice and the author identification below
    #      must be left intact in the program and in any copies.
    #   2) Any modifications to the program must be clearly identified
    #      in the source file.
    #
    #   UNIX Systems Administrator
    #   Department of Computer Science
    #   Duke University, Durham, NC
    #   Phone: 919.660.6582
    #   Email: wjsat_private
    #
    #
    # THIS SOFTWARE IS PROVIDED 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. YOU ARE RESPONSIBLE
    # FOR ANY DAMAGE THIS MIGHT DO TO YOUR MACHINES!!! IN NO EVENT SHALL THE
    # AUTHOR OF THIS PROGRAM BE LIABLE FOR DAMAGE THIS PROGRAM CAUSES.
    
    
    # Load all needed modules
    use English;
    use MD5;
    use Net::FTP;
    use FileHandle;
    use Getopt::Std;
    
    autoflush STDERR 1;
    autoflush STDOUT 1;
    
    getopts('Aa:cdE:e:Ff:g:hiL:N:np:Q:qRrS:s:vX:Z:');
    
    # account name, only users with contract accounts at sunsolve can get
    # patchdiag.xref file. The account will be "ID/passwd".
    !defined $opt_a
      ? ($account = "PUT YOUR ACCOUNT HERE")
      : ($account = $opt_a );
    
    #version number
    $version_number = "2.8";
    
    # pase the options
    $usage_message = qq|
    
        USAGE: patchreport [-A] [-a "ID/passwd"] [-cd]
                           [-E "/path/to/excluded_patches"]
                           [-e "103594 104117 105408 105616"]
                           [-Ffghi] [-N "/path/to/recommended_list"]
                           [-n] [-p "/path/to/patches"] [-Rr]
                           [-S "/path/to/CHECKSUMS"]
                           [-s "message"] [-X "/path/to/patchdiag.xref"]
    
                       -A     Prompt for account information
                       -a     SunSolve "ID/passwd"
                       -c     Prints patches which are current (UP)
                       -d     Debugging option
                       -E     "/path/to/excluded_patches"
                       -e     Exclude patch IDs (e.g. 103594 sendmail patch for sparcs)
                       -F     Force patch installation without any questions
                       -f     Arguments to fastpatch, i.e. -f nsI for -n -s -I ( see fastpatch documentation )
                              Defaults for fast patch are the following:
                               -n           Never call installpatch (by default, fastpatch will
                                            fall back to installpatch when it can't find package
                                            matches)
                               -s           Save old files so the patch can be backed out.
                                            (Works for new style patches)
                               -I           Ignore backoutpatch failures
                                            (instead, the system state is updated as if the patch
                                            has been backed out)
                       -g     Grace period for shutdown (in seconds)
                       -h     Prints this message and exits
                       -i     Install patches
                       -L     "/path/to/file_with_list_of_patches"
                       -N     "/path/to/recommended_list"
                       -n     No contract support (use Recommended patch list)
                       -p     "/path/to/patches" (default: /var/tmp/patches)
                       -Q     "/path/to/fastpatch"
                       -q     Use Casper Dik's fastpatch program to install patches
                       -R     Remove compressed patches and directories after installation,
                              but not if uncompressing to a different directory ( -Z option ).
                              In that case just clean up the uncompressed directory and leave
                              compressed patch in place.
                       -r     Retrieve patches
                       -S     "/path/to/CHECKSUMS"
                       -s     Shutdown with "Message"
                       -v     Version number
                       -X     "/path/to/patchdiag.xref"
                       -Z     "/path/to/uncompress_patches"\n
      |;
    
    # If the program is called with the -h option simply print
    # the usage message and exit.
    if (defined $opt_h){
      print "$usage_message\n";
      exit 0;
    }
    
    
    # If the program is called with the -v option simply print
    # the version, and the usage message and exit.
    if (defined $opt_v) {
      print "\n\tPatchReport version $version_number\n";
      exit 0;
    }
    # If we are called with the -i (install option) make sure
    # that we have the appropriate permissions
    if ((defined $opt_i) and ($> or $< != 0)) {
      print "\n    You must be root to install patches.";
      print "$usage_message\n";
      exit 0;
    }
    # if the account is set to the default assume that we
    # need to print an error message asking for an account.
    if ($account eq "PUT YOUR ACCOUNT HERE" and !$opt_S and !$opt_X and !$opt_n and !$opt_A ) {
      print qq|
               You must have a SunSolve account to use this script. The
               -n option can be used to by-pass this check, and use the
               Recommended patch list instead of the patchdiag.xref file.
               You can hard code the account and ID into the script. |;
      print "$usage_message\n";
      exit 0;
    }
    # Let's figure out where fastpatch is located if possible. If not just exit.
    if (defined $opt_q ) {
      if (defined $opt_Q) {
        $INSTALL_PATCH_PROG = $opt_Q;
        if ( ! -e $INSTALL_PATCH_PROG ) {
           print qq|
              Fastpatch was not found in that location. Please use -Q to
              the specify correct path to the fastpatch program.\n\n|;
           exit 1;
        }
      } elsif ( !defined $opt_Q) {
        $INSTALL_PATCH_PROG = `which fastpatch`;
        if ($INSTALL_PATCH_PROG =~ /no fastpatch in/) {
          print qq|
              Fastpatch program not found. Please use -Q to specify
              where the fastpatch program is located or add its
              directory to your path.\n\n|;
        exit 1;
        }
      }
    } else {
      $INSTALL_PATCH_PROG = "./installpatch";
    }
    
    # When summomed with the -A option the user will be asked
    # for the account name and password for SunSolve's FTP site
    if ( defined $opt_A) {
      print qq|
              Please provide the account and password in the form "ID/passwd"
              \n\naccount/passwd? |;
      chomp($account = <STDIN>);
    }
    # The -p option allows for the output of the patches downloaded
    # to go into another directory. This is good for large sites that
    # share a common patch directory.
    if (!defined $opt_p) {
      $patch_dir = "/var/tmp/patches";
    } elsif (defined $opt_p) {
      $patch_dir = "$opt_p";
    }
    
    # Setp the arguments to fast patch. By default use -n -s -I
    #       -n              Never call installpatch (by default, fastpatch will
    #                       fall back to installpatch when it can't find package
    #                       matches)
    #       -s              Save old files so the patch can be backed out.
    #                       (Works for new style patches)
    #                       This should be optional for PatchReport
    #
    #       -I              Ignore backoutpatch failures
    #                       (instead, the system state is updated as if the patch
    #                       has been backed out)
    
    if ( defined $opt_f and defined $opt_q ) {
      map {  $FAST_PATCH_ARGS = $FAST_PATCH_ARGS  . "-$_ " } split(//,$opt_f);
    } elsif (defined $opt_q and !defined $opt_f ) {
      $FAST_PATCH_ARGS = "-n -s -I";
    }
    
    # Get some information about who we are
    @uname = split ' ',`uname -a`;
    $uname[2] =~ s/^5/2/ or die "can't convert SunOS-Solaris version number";
    if ($uname[5] eq "i386") { $os = "$uname[2]_x86";} else { $os = "$uname[2]";}
    
    # Print a nice little message to let the users know
    # what is going on when the script first starts up
    print qq|\n
           Analyzing needed patches on your machine, this might take
           a minute or two depending on the options you chose, and/or
           your net connection.\n\n|;
    
    ################################################
    ############## File Retrieval ##################
    ################################################
    
    # If the -X option or the -S options are not used this means that
    # the patchdiag.xref file and/or the CHECKSUMS file are not stored
    # locally. Since this is the case we have to get the files from the net.
    if ((!defined $opt_X or !defined $opt_S) and !$opt_n){
      $ftp = Net::FTP->new("sunsolve.sun.com", Debug => $opt_d ? 1 : 0);
      $ftp->login("sunsolve","sunmicro","$account") ;#or warn "can't login";
      # If the -n option is used then we need to go and get the Recommended
      # patch list. We need to do this in regular anonymous mode.
    } elsif (defined $opt_n) {
      $ftp = Net::FTP->new("sunsolve.sun.com", Debug => $opt_d ? 1 : 0);
      $ftp->login() ;#or warn "can't login, bad password perhaps\?";
    }
    
    # If the -X option or the option is used this means that the
    # patchdiag.xref file are stored locally. Since this is the
    # case we do not have to get the files from the net.
    if (defined $opt_X and !$opt_n) {
      $xref_fd = new FileHandle "$opt_X", "r";
    } elsif (!defined $opt_X and !defined $opt_n) {
      # -X was not used so we need to get the file from
      # Sunsolve's site
      $xref_fd = new FileHandle "/tmp/patchdiag_$$", "w+";
      $ftp->get("patchdiag.xref", $xref_fd);
    } elsif (defined $opt_n and !defined $opt_N) {
      # If -n was used, and not -N then we need to retrieve the
      # file from the net.
      $recommended_fd  = new FileHandle "/tmp/Recommended_$$", "w+";
      $ftp->cwd("/pub/patches");
      $ftp->get("$os\_Recommended.README", $recommended_fd);
    } elsif (defined $opt_n and defined $opt_N) {
      # if -n is used with -N then that means that the file is
      # stored locally. Set the file handle to the argument
      # given to -N. This should be the path to the
      # Recommended list.
      $recommended_fd  = new FileHandle "$opt_N", "r";
    }
    if (!$opt_n) {
      # Make sure that we are not working in non-contract mode
      # if we are not, and the -S option is defined the
      # the path to the checksums file should be the argument
      # given to -S
      if (defined $opt_S) {
        $checksum_fd  = new FileHandle "$opt_S", "r";
      } else {
        $checksum_fd  = new FileHandle "/tmp/CHECKSUMS_$$", "w+";
        $ftp->get("CHECKSUMS", $checksum_fd);
      }
    }
    
    # play it again sam
    # if the -n option was not used putting us into non-contract mode
    # then we likely retreived the files from the net. We have to
    # go to the beginning to read the entire contents of the file.
    if (!defined $opt_n) {
      seek $xref_fd,0,0;
      seek $checksum_fd,0,0;
    } elsif ($opt_n) {
      seek $recommended_fd,0,0;
    }
    
    ################################################
    ############ Formatting and parsing ############
    ############ for the needed patches ############
    ################################################
    
    format patch_top =
    
    Patch-ID  Security Recommended ID Description
    --------- -------- ----------- -- ------------------------
    .
    format patch_out =
    @<<<<<<<< @<<<<<<< @<<<<<<<<<< @< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    "$x_id-$x_rev", $security, $recommended, $showrev{$x_id}, $x_desc
    .
    
    $^="patch_top";
    $~="patch_out";
    
    # We need to get the current patches on the machine. the command showrev -p
    # will get the desired information. The map function will take every occurance
    # found in the showrev -p and preform the block operation on it. In this case
    # the operation is to double split the output, and then create an associative
    # array.
    map {($s_id,$s_rev) = split '-',(split)[1];$showrev{$s_id} = $s_rev} `showrev -p|sort`;
    
    # Show patches taht apply to add-on programs like Disksuite and veritas if
    # we are called with the -o option
    
    # If we are in contract mode then we will need to take the output of the
    # patchdiag.xref file. We split the file, and then test to see if we have
    # the patch-id from the showrev -p array. We also do a little formatting
    # depending on whether or not the file is recommended or a security patch
    # or both. We also make use of the write function, to keep things formatted
    # nicely.
    if (!defined $opt_n) {
      while(<$xref_fd>){
        map {($x_id,$x_rev,$x_rec,$x_sec,$x_os,$x_arch,$x_desc) = (split(/\|/,$_))[0,1,3,4,7,8,10]} $_;
        if ((!defined $showrev{$x_id} or $showrev{$x_id} < $x_rev)
            and ($x_arch =~ /$uname[5]\;|$uname[5]\.$uname[4]\;|all\;$uname[5]\;|all\;/)
            and ($x_os eq "$os")) {
          if ($x_rec eq "R") { local $recommended = "Recommended ";} else {local $recommended = "     N/A    ";}
          if ($x_sec eq "S") { local $security    = "Security ";}    else {local $security    = "   N/A   ";}
          push @needed, "$x_id-$x_rev";
          $patch_description{"$x_id-$x_rev"} = "$x_desc";
          write;
        }
        # if we get a hit then that means we are current. So we should
        # print up in the patch revision place.
        elsif ((defined $showrev{$x_id} or $showrev{$x_id} = $x_rev)
               and ($x_arch =~ /$uname[5]\;|$uname[5]\.$uname[4]\;|all\;$uname[5]\;|all\;/)
               and ($x_os eq "$os") and defined $opt_c)  {
          if ($x_rec eq "R") { local $recommended = "Recommended ";} else {local $recommended = "     N/A    ";}
          if ($x_sec eq "S") { local $security    = "Security ";}    else {local $security    = "   N/A   ";}
          $showrev{$x_id} = "UP";
          write;
        }
      }
      # If we are using the -n non-contract mode then don't do too much.
      # just parse the file, and get the basics like the patch-id
    } elsif (defined $opt_n) {
      while (<$recommended_fd>) {
        ($x_id,$x_rev) = map{split '-',(split)[0]}  grep /^\d{6}/,$_ or next;
        ($junk,@x_desc) = split;
        if (!$showrev{$x_id} or $showrev{$x_id} < $x_rev) {
          push @needed, "$x_id-$x_rev";
          $security = ""; $recommended = "";
          $x_desc = join ' ', @x_desc;
          $patch_description{"$x_id-$x_rev"} = $x_desc;
          write;
        }
        # if we get a hit then that means we are current. So we should
        # print up in the patch revision place.
        elsif ((defined $showrev{$x_id} or $showrev{$x_id} = $x_rev) and defined $opt_c) {
          $showrev{$x_id} = "UP";
          write;
        }
      }
    }
    
    ################################################
    ############## MD5 checksum test ###############
    ################################################
    
    # Now, if we weren't run with the -n option, we parse the checksums
    # file making an array of the values of patch-id to the actual
    # MD5 checksum as calculated by Sun. We need to go into the
    # multiline mode so we set the record separator (RS).
    
    if (!$opt_n) {
      $RS='';
      map {($patch_checksum_id) = /^(\d{6}-\d{2}).tar.Z/m;
           ($patch_checksum) = /MD5: (.*)/;
           if ($patch_checksum_id ne "") {
             $actual_checksum{$patch_checksum_id} = $patch_checksum;
           }
         } <$checksum_fd>;
      $RS="\n";
    }
    
    format get_top  =
    Patch-ID    Checksum status       Description
    ---------   ------------------    --------------------
    .
    format get_out =
    @<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    $get_status, $checksum_status,$patch_description{$_}
    .
    
    # If we are called with the -r switch get the patches, and check the checksums
    # for each file we will calculate our own checksums, and compare then. If they
    # match then they can be installed. If not print an error message. This will
    # be done for all of the needed patches from, as determined from above.
    
    $md5 = new MD5;
    
    if (defined @needed and defined $opt_r) {
      mkdir "$patch_dir",0755;
      print "\n**Retrieving Patches**\n";
      print "Patch-ID    Checksum status       Description\n---------   ------------------    --------------------\n";
      $^ = "get_top";
      $~ = "get_out";
      map {
        $get_status = "$_\t";
        $ftp->binary;
        $~ = "get_out";
        $ftp->get("$_.tar.Z","$patch_dir/$_.tar.Z");
        if (!defined $opt_n) {
          $subject = new FileHandle "$patch_dir/$_.tar.Z";
          if (!defined $opt_n) {
            $md5->reset();
            $md5->addfile($subject);
            $retrieved_checksum = $md5->hexdigest();
            $calculated_checksum{$_} = $retrieved_checksum;
            ($actual_checksum{$_} eq "$retrieved_checksum")
              ? ($checksum_status   = "checksum match")
              : ($checksum_status   = "*CHECKSUM FAILED*");
          }
        }
        write
      } @needed;
      # We also need to check the checksum if the file is stored on
      # a local file system, hence called without the -r option.
    } elsif (defined @needed and defined $opt_i and !defined $opt_r) {
      map {
        $subject = new FileHandle "$patch_dir/$_.tar.Z";
        if (!defined $opt_n) {
          $md5->reset();
          $md5->addfile($subject);
          $retrieved_checksum = $md5->hexdigest();
          $calculated_checksum{$_} = $retrieved_checksum;
          ($actual_checksum{$_} eq "$retrieved_checksum")
            ? ($checksum_status   = "checksum match")
            : ($checksum_status   = "*CHECKSUM FAILED*");
        }
      } @needed;
    
    }
    # If we do not need patches, then you are pretty up on things.
    # print a nice message.
    if (!defined @needed) {
      print qq|
    
        Congratulations you do not need any patches installed.
        Send this note to your boss, and ask for a raise!!!
    
          |;
    }
    
    # This avoids an error if the ftp module was never opened.
    if (!defined $opt_X or !defined $opt_S){
      $ftp->quit;
    }
    # Clean up the files that were downloaded from the net.
    if (!defined $opt_n) {
      unlink "/tmp/patchdiag_$$", "/tmp/CHECKSUMS_$$";
    } else {
      unlink "/tmp/Recommended_$$";
    }
    
    format install_top =
    Patch-ID    Install status        Description
    ---------   ---------------       --------------------
    .
    format install_out =
    @<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    $installing ,$patch_install_status, $patch_description{$patch_to_install}
    .
    
    
    ################################################
    ############# Patch installation ###############
    ################################################
    
    if (defined @needed and defined $opt_i and !defined $opt_n) {
      &question_patch_install_sub;
    } elsif (defined $opt_n and defined $opt_i) {
      &question_patch_install_sub;
    }
    
    ################################################
    ############# Shutdown message #################
    ################################################
    
    if ( defined $opt_s) {
      print "\n\n**SHUTTING DOWN WITH MESSAGE: $opt_s\n\n";
      `/usr/sbin/shutdown -y -g$opt_g -i6 "$opt_s" &`;
    }
    
    sub question_patch_install_sub {
      if (!defined $opt_F) {
        # Print an ominous message to let the user know this might
        # be a bad idea. If they still want to do it, they probably
        # know what they are doing
        print qq|
                ** Installing all patches without checking them first **
                ** can have negative consequences. I am assuming that **
                ** you know this, and think that all of these patches **
                ** are a good idea. Using the -F option will turn off **
                ** this message.                                      **
            \n|;
        if (!defined $opt_L) {
          # Get a confirmation or a list of patches to install
          print "Which patches do you want to install (all/none/list of patches) ";
          chomp(local $answer_install_patch = <STDIN>);
          if ( $answer_install_patch eq "all") {
            &install_patch_sub;
          } elsif ($answer_install_patch eq "none") {
            print "\n\tExiting install procedure\n";
            exit 0;
          } elsif ($answer_install_patch =~ /^10/) {
            @needed = split ' ',$answer_install_patch;
            &install_patch_sub;
          } else {
            print "\n\tCan't determine answer, aborting installation procedure\n";
            exit 0;
          }
        }
        elsif ( defined $opt_L) {
          print "Would you like to install all patches listed in $opt_L? (yes/no)\n";
          chomp(local $answer_install_patch = <STDIN>);
          if ($answer_install_patch =~ "n") {
            print "\n\tExiting install procedure\n";
            exit 0;
          }
          elsif ($answer_install_patch =~ "y") {
            &install_patch_sub;
          }
        }
        # If called with the -F flag then skip the formality, and just install
        # the patches.
      } elsif (defined $opt_F) {
        &install_patch_sub;
      }
    }
    
    sub install_patch_sub {
     if (defined $opt_q ) {
      print "\n\n**Installing patches with fastpatch**\n";
      }
      else {
        print "\n**Installing Patches (this can take a while)**\n";
      }
      if (!defined $opt_q ) {
        print "\nPatch-ID    Install status        Description\n";
        print "---------   ---------------       -------------------- \n";
      }
      # for all of the patches left in the @needed array we do a regulat old
      # installation. Just uncompress the patch, cd into the directory and run
      # the installpatch program. Also check the return code of the installpatch
      # program. If the return code is something other than 0 then grep the error
      # code from the installpatch program and print it on the install status
      # column of the output.
      $^ = "install_top";
      $~ = "install_out";
      if (defined $opt_E) {
        $excluded_patches_fd = new FileHandle "$opt_E", "r";
        chomp(@excluded_patches = <$excluded_patches_fd>);
      }
      if (defined $opt_e) {
        push @excluded_patches,split ' ',$opt_e;
      }
      if (defined $opt_L) {
        $needed_patches_fd  = new FileHandle "$opt_L", "r";
        chomp(@needed = <$needed_patches_fd>);
        print "\n\nInstalling patches listed in $opt_L\n\n";
      }
      foreach $patch_to_install (@needed) {
        # Make sure that we do not have any white space in the patch-id from the
        # possible input on the command line.
        $patch_to_install =~ s/\s//g;
        $skip_this_patch = 0;
        if (defined $opt_e or defined $opt_E) {
              map { (substr($patch_to_install,0,6) eq substr($_,0,6)) ?  $skip_this_patch = 1 : ""} @excluded_patches;
        }
        if (($actual_checksum{$patch_to_install} eq $calculated_checksum{$patch_to_install}) and
               ($patch_to_install ne "") and ($skip_this_patch != 1)) {
          if (defined $opt_Z) {
            chdir "$opt_Z";
            `/usr/bin/uncompress < $patch_dir/$patch_to_install.tar.Z | /bin/tar xf -`;
            chdir "$opt_Z/$patch_to_install";
          }
          else {
            chdir "$patch_dir";
            `/usr/bin/uncompress < $patch_to_install.tar.Z | /bin/tar xf -`;
            chdir "$patch_dir/$patch_to_install";
          }
          $installing = "$patch_to_install";
          if (defined $opt_q and defined $opt_Z) {
            exec `$INSTALL_PATCH_PROG -p $opt_Z $FAST_PATCH_ARGS $patch_to_install`;
          } elsif (defined $opt_q and !defined $opt_Z ) {
            exec `$INSTALL_PATCH_PROG -p $patch_dir $FAST_PATCH_ARGS $patch_to_install`;
          } elsif (!defined $opt_q) {
            `$INSTALL_PATCH_PROG .`;
          }
          if ($? != 0 and !defined $opt_q){
            $error = $?/256;
            $installpatch_fd  = new FileHandle "./installpatch", "r";
            map { $patch_install_status = "$1" if /\#\t\t$error\t(.*)/} <$installpatch_fd>;
          }
          elsif ($? != 0 and defined $opt_q) {
            $patch_install_status = "*NOT INSTALLED*";
          } else {
            $patch_install_status = "Patch installed\t";
            if (defined $opt_R and !defined $opt_Z) {
              chdir "$patch_dir";
              `rm -rf $patch_to_install`;
              unlink "$patch_to_install.tar.Z";
            }
            elsif (defined $opt_R and defined $opt_Z) {
              chdir "$opt_Z";
              `rm -rf $patch_to_install`;
            }
          }
        } elsif ($actual_checksum{$patch_to_install} ne $calculated_checksum{$patch_to_install}
                and  ($skip_this_patch != 1) ) {
          $patch_install_status = "*NOT INSTALLED*"; $installing = "$patch_to_install";
        }
        elsif ($skip_this_patch == 1) {
          $patch_install_status = "*EXCLUDED PATCH*"; $installing = "$patch_to_install";
        }
        if (defined $opt_q and $skip_this_patch != 1){
          print "Fastpatch messages for $patch_to_install:\n-----------------------------------------\n\n";
          write;
        }
        elsif (!defined $opt_q) {
          write;
        }
      }
    }
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:28:07 PDT