#!/usr/bin/perl ############################################################################ # # # wrf_update.pl # # # # The wrf_update.pl script is to be used to update your STRC WRF EMS # # distribution as patches and updates become available on the SOO/STRC # # ftp server. Running wrf_update is easy, either from the command # # line or within a cron. Note that if you decide to run wrf_update # # from a cron you must uncomment the "$ENV{WRF}" setting below and # # make sure that it points to your WRF installation. If you are # # running wrf_update from a command line then $WRF should already be # # set as one of your environment variables. # # # # Regardless of the method you choose, make sure to read and edit # # the $WRF/data/conf/config/wrf_update/wrf_update.conf file. Also # # run "wrf_update.pl --help for lots of great information about # # running the script. # # # $ENV{WRF} = "/usr1/wrf"; # <-- Change if necessary # # # # Running wrf_update from a cron: # # # # The recommended method of running this script is via a cron, simply # # because this step will insure that you have the latest greatest # # version of the package where old bugs have been fixed and new ones # # added. Here is a sample crontab entry: # # # # 10 04 * * * WRFDIR/strc/wrf_update.pl --update --install >> WRFDIR/logs/wrf_update.log 2>&1 # # # # Remember to edit the configuration file and read the help menu. # # # # Recent Log: # # # # R.Rozumalski - NOAA/NWS/FDTB/STRC # # # # May 2006 Version 002a - The 0.1% Inspiration, 99.9% Perspiration Release # # # ############################################################################ # use strict; require 5.6.0; use vars qw (%Option %Update); &user_opts; &patch_restore if $Update{RESTORE}; &patch_clean if $Update{CLEAN}; &patch_version; &patch_list if $Update{UPDATE} or $Update{LSTALL} or $Update{LIST}; exit if $Update{LSTALL} or $Update{LIST}; &patch_get if $Update{UPDATE}; &patch_install if $Update{INSTALL}; exit; # # ------------------------------------------------------------------------------ # sub user_opts { use Getopt::Long; use Cwd; # Begin by assigning the script name # $Option{EXE} = "wrf_update.pl"; # Processes options passed to the clean script. # unless ($ENV{WRF} and -e "$ENV{WRF}/WRF.cshrc") { print "\n\n ERROR: Your WRF environment variable is incorrectly set.\n\n";exit; } &help_menu if !scalar(@ARGV); GetOptions ( "install!" => \$Option{INSTALL}, # Do [not] the install "update" => \$Option{UPDATE}, # Do the update "cumulative"=> \$Option{CUMULATIVE}, # install cumulativeupdate "restore" => \$Option{RESTORE}, # Restore from *.prev files "clean" => \$Option{CLEAN}, # Clean any *.prev files "delete!" => \$Option{DELETE}, # Do [not] delete update tar files after install "force" => \$Option{FORCE}, # Install even if already installed "version" => \$Option{VERSION}, # Print out STRC EMS Version number "listall" => \$Option{LSTALL}, # List all available updates on strc site "list" => \$Option{LIST}, # List updates not installed "nomail" => \$Option{NOMAIL}, # Don't send any mail "uconf!" => \$Option{UCONF}, # [Don't] Update the user domain config files "help" => \$Option{HELP} ) or die &help_menu; &help_menu if $Option{HELP}; # Read user configuration file # my $conf = "$ENV{WRF}/data/conf/config/wrf_update/wrf_update.conf"; open INFILE => $conf or die " BUMMER: WRF update configuration file not found ($conf) - $! \n"; while () { next if /^#|^$|^\s+/; s/ //g;s/\t//g;s/\n//g; my ($var, $value) = split(/\s*=\s*/, $_, 2); $Update{$var} = $value; } close INFILE; $Update{DELETE} = $Update{DELETE} =~ /^Y/i; $Update{INSTALL} = $Update{INSTALL} =~ /^Y/i; $Update{UCONF} = $Update{UCONF} =~ /^Y/i; # Override configuration file options with command line ones # for my $opt ( keys %Option ) { $Update{$opt} = $Option{$opt} if defined $Option{$opt}; } $Update{VERBOSE} = $Update{LIST} or $Update{LISTALL} ? 1 : 0; $Update{UPDATE} = 0 if ($Update{LIST} or $Update{LISTALL}); $Update{INSTALL} = 0 if ($Update{LIST} or $Update{LISTALL}); if ($Update{VERSION}) { for my $opt ( keys %Option ) {delete $Update{$opt};} $Update{VERSION} = 1; } print "\n\n SOO/STRC WRF Environmental Modeling System (EMS) Update Program\n"; return; } # ------------------------------------------------------------------------------ # sub patch_list { use Net::FTP; my $ftp; # Key to WRF update naming convention # # STRCWRF_U02010001a.tgz # # STRCWRF - STRC WRF EMS Update # _U|_C - Individual Update|Cumulative Update through specified release # - New Cumulative update for each STRC WRF EMS version number # 020100 - WRF Version 2.1.0 (02.01.00) # 01 - STRC WRF EMS Release 1 (01) # a - Update level a # die "\n OOPS: You will need to create a .netrc file with the soostrc ftp\n", " server login information if you want to download updates.\n\n" unless -e "$ENV{HOME}/.netrc"; if (system "/bin/grep -i -q soostrc $ENV{HOME}/.netrc") { die "\n OOPS: You will have to add login information for the STRC ftp\n", " server to your .netrc file if you want to download updates.\n\n"; } print "\n * Obtaining list of available STRC WRF updates.\n" if $Update{VERBOSE}; if ( !( $ftp=Net::FTP->new(lc($Update{UPDATEHOST})) ) ) { die "\n CONNECTION ERROR: $@ \n\n"; } if ( ! $ftp->login() ) { die "\n LOGIN ERROR: ",$ftp->message(),"\n"; } $ftp->binary(); my @patch_list = sort $ftp->ls($Update{UPDATEDIR}); # Exit if the update directory is empty # unless (@patch_list) { print "\n There are no updates currently available on the ftp server - Exit \n\n"; exit; } # reduce the update list to just the file name and sort # foreach (@patch_list) { my @tmp = split /\// => $_; for (pop @tmp) { if ($Update{CUMULATIVE}) { next unless /^STRCWRF_C/; } else { next unless /^STRCWRF_U/; } push @{$Update{PATCHES}} => $_; } } @{$Update{PATCHES}} = sort @{$Update{PATCHES}} if defined @{$Update{PATCHES}}; if ($Update{LSTALL} and @{$Update{PATCHES}} ) { print "\n * All available updates located on $Update{UPDATEHOST}:\n\n"; foreach (@{$Update{PATCHES}}) {print " $_\n";} print "\n"; } elsif ($Update{LSTALL}) { print "\n No updates currently available\n\n"; exit; } # Determine which updates need to be installed # my @patch_updates; my $curlev = $Update{CUMULATIVE} ? "STRCWRF_C$Update{REVSTR}\.tgz" : "STRCWRF_U$Update{REVSTR}\.tgz"; foreach (@{$Update{PATCHES}}) { if ($Update{FORCE}) { push @patch_updates, $_; } else { push @patch_updates, $_ if $_ gt $curlev; } } if (@patch_updates) { @{$Update{PATCHES}} = sort @patch_updates; if ($Update{LIST}) { print "\n * Updates needed to bring your STRC WRF EMS up-to-date.\n\n" if $Update{VERBOSE}; foreach (@{$Update{PATCHES}}) {print " $_\n";} } unless ($Update{UPDATE}) { print "\n Hint: If you want to download and install the above updates use \"$0 --update --install\"\n" if $Update{VERBOSE}; } print "\n"; } else { print "\n Congratulations - Your STRC WRF EMS is up-to-date.\n\n"; exit; } return; } # ----------------------------------------------------------------------- # sub patch_clean { print "\n Tidying up your STRC WRF EMS by deleting previous installations\n"; foreach (`/usr/bin/find $ENV{WRF} -name "*.prev"`) {system "rm -f $_"} print "\n All clean - EXIT\n\n"; exit; } # ----------------------------------------------------------------------- # sub patch_restore { use File::stat; my $release; my $verfile = "$ENV{WRF}/strc/.release.prev"; if (-e $verfile) { open INFILE => $verfile; while () { next if /^#|^$|^\s+/; s/\t/ /g;s/\n//g; s/\s+/ /g; my @array = split / /=> $_; $release = $array[3]; } close INFILE; print "\n Restoring STRC WRF EMS to version $release\n\n"; } else { print "\n Restoring STRC WRF EMS to previous release\n\n"; } my $date = time; foreach (`/usr/bin/find $ENV{WRF} -name "*.prev"`) { chomp; my $stat = stat $_; $date = $stat->atime if $stat->atime < $date; (my $orig = $_) =~ s/.prev//g; system "chmod 755 $_"; print " * Restoring: $orig\n"; system "mv $_ $orig"; } # Now go through the user's configuration files. # print "\n Restoring user configuration files in the runs directory\n\n"; my ($sec, $min, $hr, $dy, $mo, $yr, $wday, $yday, $isdst) = gmtime $date; $yr = $yr + 1900; $mo = $mo + 1; $mo = "0$mo" if length $mo == 1; $dy = "0$dy" if length $dy == 1; my $datestr = "$yr$mo$dy"; foreach (`/usr/bin/find $ENV{WRF} -name "*.$datestr"`) { chomp; (my $orig = $_) =~ s/.$datestr//g; system "chmod 755 $_"; print " * Restoring: $orig\n"; system "mv $_ $orig"; } print "\n Restoration Complete - EXIT\n\n"; exit; } # ----------------------------------------------------------------------- # sub patch_get { use Net::FTP; use File::stat; my $ftp; my $patchdir = "$ENV{WRF}/$Update{PATCHDIR}"; system "mkdir $patchdir" unless -e "$patchdir"; # Open log file in the patchdir directory # print "\n * Retrieving requested STRC WRF EMS updates\n\n"; if ( !( $ftp=Net::FTP->new(lc($Update{UPDATEHOST})) ) ) { die "\n CONNECTION ERROR: $@ \n\n"; } if ( ! $ftp->login() ) { die "\n LOGIN ERROR: ",$ftp->message(),"\n"; } $ftp->binary(); foreach (@{$Update{PATCHES}}) { # Does the patch already exist and is the correct size? # if (-e "$patchdir/$_") { my $sf = stat("$patchdir/$_"); my $rsize=$ftp->size("$Update{UPDATEDIR}/$_"); if ($sf->size != $rsize) { print " Downloading again: $_\n"; unless ( $ftp->get("$Update{UPDATEDIR}/$_","$patchdir/$_") ) { print " FTP ERROR: ",$ftp->message(),"\n"; } } else { print " UPDATE: $_ is already downloaded.\n"; } } else { print " Downloading: $_\n"; unless ( $ftp->get("$Update{UPDATEDIR}/$_","$patchdir/$_") ) { print " FTP ERROR: ",$ftp->message(),"\n"; } } } $Update{INSTALL} = 0 unless @{$Update{PATCHES}}; # Make sure the updates were downloaded. If not then do not install. # foreach (@{$Update{PATCHES}}) { unless (-e "$patchdir/$_") { print " ERROR: $patchdir/$_ not found - No update installation will occur"; $Update{INSTALL} = 0; } } print "\n"; return; } # ----------------------------------------------------------------------- # sub patch_install { chdir $ENV{WRF}; my $patchdir = "$ENV{WRF}/$Update{PATCHDIR}"; my $patchlog = "$patchdir/patch_install.log"; # Determine the machine architecture for binaries # choices are PIII, Athlon, X64, and x32 # my ($nodset1, $nodset2, $nodset3); opendir DIR => "$ENV{WRF}/bin" or die "\n BUMMER: You've got directory problems ($ENV{WRF}/strc) - $!.\n\n"; my @arch = grep /x32|x64|xpc|athlon/ => readdir(DIR); my $dset = $arch[0]; if ($dset) { my @nodset; my $n = 0; foreach ( ".xpc", ".x32", ".x64", ".athlon" ) {$nodset[$n++] = $_ unless /$dset/;} $nodset1 = $nodset[0]; $nodset2 = $nodset[1]; $nodset3 = $nodset[2]; } else { my $proc = `/bin/uname -p`; if ($proc =~ /x86_64/) { $dset = ".x64"; $nodset1 = ".x32"; $nodset2 = ".athlon"; $nodset3 = ".xpc"; } elsif ($proc =~ /i686/i) { $dset = ".x32"; $nodset1 = ".x64"; $nodset2 = ".athlon"; $nodset3 = ".xpc"; open FILE, "/proc/cpuinfo"; my @lines = ; close FILE; foreach (@lines) { if (/Pentium III/i) { $dset = ".xpc"; $nodset1 = ".x64"; $nodset2 = ".x32"; $nodset3 = ".athlon"; } elsif (/Athlon/i) { $dset = ".athlon"; $nodset1 = ".xpc"; $nodset2 = ".x64"; $nodset3 = ".x32"; } } } elsif ($proc =~ /Athlon/i) { $dset = ".athlon"; $nodset1 = ".x64"; $nodset2 = ".x32"; $nodset3 = ".xpc"; } else { $dset = ".xpc"; $nodset1 = ".athlon"; $nodset2 = ".x64"; $nodset3 = ".x32"; } } # If the update(s) were downloaded manually then the update files # must be added to the PATCHES list. # unless ($Update{UPDATE}) { opendir PD => "$patchdir"; @{$Update{PATCHES}} = grep /STRCWRF_/ => readdir PD; print "\n"; } # Append to the log file in the patchdir directory # open LOG => ">$patchlog"; print LOG "\n\n Greetings!\n\n". " The following update files were installed on your STRC WRF EMS:\n\n"; # Make sure the files are in correct order # @{$Update{PATCHES}} = sort @{$Update{PATCHES}}; my @tmpfiles; foreach (@{$Update{PATCHES}}) { print LOG " * Installing incremental update file: $_\n\n"; print " * Installing incremental update file: $_\n\n"; # Get the listing of the files in the tarball # my @files; my @rmfiles; for (`tar -ztf $ENV{WRF}/$Update{PATCHDIR}/$_`) { s/\n//g; if (/$nodset1|$nodset2|$nodset3/) { push @rmfiles, $_; } else { push @files, $_; } push @{$Update{CONFIGS}}, $_ if /\.conf/ and !/gribinfo/ and !/run_physics/ and !/wrf_update/; } foreach (@files) { next if -d "$ENV{WRF}/$_" ; next if /release/; next if /logs/; (my $file = $_) =~ s/$dset//g; print LOG " $file\n"; } # Backup each file that will be replaced. A .tmp will be appended # to the name of the origional file until the install is complete # at which time the files will be renamed to *.prev # foreach (@files) { (my $file = $_) =~ s/$dset//g; if (-f $file) { system "mv $file $file.tmp"; push @tmpfiles, "$file.tmp"; } } # Unpack the patch from the $WRF directory. # if (system "tar -xzf $ENV{WRF}/$Update{PATCHDIR}/$_") { print " ERROR installing update: $_ - EXIT before more damage is done.\n"; print " Returning installation to pre-updated state\n"; print LOG " ERROR installing update: $_ - EXIT before more damage is done.\n"; print LOG " Returning installation to pre-updated state\n"; foreach (@tmpfiles) { if (-e $_ ) { (my $orig = $_) =~ s/\.tmp//g; system "mv $_ $orig"; } } exit; } # Do post install configuration # foreach my $file (@files) { next if -d "$ENV{WRF}/$file"; next if -l "$ENV{WRF}/$file"; if ($file =~ /$dset/) { (my $nfile = $file) =~ s/$dset//g; system "mv $ENV{WRF}/$file $ENV{WRF}/$nfile"; next; } next if (-B "$ENV{WRF}/$file" or -l "$ENV{WRF}/$file"); open FILE, "$ENV{WRF}/$file"; my @lines = ; close FILE; open FILE, ">$ENV{WRF}/$file"; unless ($file =~ /wrf_update.pl/) { foreach (@lines) { s/WRFDIR/$ENV{WRF}/g;s/SYSCPUS/$ENV{NCPUS}/g; print FILE $_;} } close FILE; } # Delete the unwanted files # foreach (@rmfiles) {system "rm -fr $_";} system "rm -fr $ENV{WRF}/bin$dset >& /dev/null" if -e "$ENV{WRF}/bin$dset"; # Update the user's configuration if so desired # if ($Update{UCONF} and @{$Update{CONFIGS}}) { print "\n\n * Updating user domain configuration files\n"; my $date = `date +%Y%m%d`; opendir DIR => "$ENV{WRF_RUN}"; my @domains = grep { $_ ne '.' and $_ ne '..' and ! -d $_ } readdir DIR; foreach my $domain (@domains) { next unless -e "$ENV{WRF_RUN}/$domain/static"; foreach my $config (@{$Update{CONFIGS}}) { next if /run_physics/; my %Conf; my @list = split /\//, $config; my $conf = "$list[-2]/$list[-1]"; my $update = "$ENV{WRF}/$config"; my $uconf = "$ENV{WRF_RUN}/$domain/conf/$conf"; system "cp $uconf $uconf\.$date" unless -e "$uconf\.$date"; open UPDATE => "$update"; my @lines = ; close UPDATE; next unless open UCONF => "$uconf"; while () { next if /^#|^$|^\s+/; s/ //g;s/\t//g;s/\n//g; my @tlist = split /=/ => $_; $Conf{$tlist[0]} = $tlist[1]; } close UCONF; open UCONF => ">$uconf"; foreach (@lines) { s/\n//g; if ( s/^\s*(\w*)\s*=//i ) { my $key = uc $1; s/\n//g;s/^\s+//g;s/,*$//g; $_ = sprintf ("%-14s = %s",$key,$_); } print UCONF "$_\n"; } close UCONF; } } } # Update the logs file with information on the patch # &patch_version; rename "$ENV{WRF}/logs/update.log" => "$ENV{WRF}/logs/$Update{REVSTR}\_update.log"; } # Foreach patch loop # Finally, rename the .tmp file to .prev for future use, if any # print "\n * Saving previous version of updated files.\n"; foreach (@tmpfiles) { (my $file = $_) =~ s/$dset//g; if (-e $file ) { (my $prev = $file) =~ s/\.tmp//g; system "mv $file $prev.prev"; system "touch $prev.prev"; } } my $str = sprintf ("\n Success installing STRC WRF EMS update%s\n\n",my $n = scalar @{$Update{PATCHES}} > 1 ? "es!" : "!"); print "\n\n Update details can be found in $ENV{WRF}/logs/$Update{REVSTR}\_update.log\n"; print $str; print LOG $str; print LOG "\n\n Update details can be found in $ENV{WRF}/logs/$Update{REVSTR}\_update.log\n"; print LOG " You may rest peacefully again - Your WRF EMS\n\n"; close LOG; patch_mail($patchlog) unless $Update{NOMAIL}; patch_version(1); system "rm -fr $ENV{WRF}/$Update{PATCHDIR}" if $Update{DELETE}; return; } # ----------------------------------------------------------------------- # sub patch_version { my $update = shift; $Update{REVSTR} = ""; my $release; # Get version number from $WRF_STRC directory .release file # my $verfile = "$ENV{WRF}/strc/.release"; open INFILE => $verfile or die " BUMMER: STRC WRF Version file not found - ($verfile) - $! \n"; while () { next if /^#|^$|^\s+/; s/\t/ /g;s/\n//g; s/\s+/ /g; my @array = split / /=> $_; $release = $array[3]; } close INFILE; if ($update) { print "\n Current STRC WRF EMS release is now $release\n\n" if $Update{VERBOSE}; } else { print "\n Current STRC WRF EMS release is $release\n" if $Update{VERSION} or $Update{VERBOSE}; } my @array = split /\./ => $release; foreach (@array) { if (/\d+/) { if ($_ < 10) { $_ = "0$_"; } } $Update{REVSTR} = "$Update{REVSTR}$_"; } return; } #---------------------------------------------------------------------------------------------------- # sub patch_mail { my $log = shift; my $exe = "/bin/mail"; my $mail = $Update{MAILTO}; exit unless $mail; unless (-e $exe) { print " SEND MAIL ERROR: $exe was not found\n"; exit; } my $subject = "\"STRC WRF EMS Update Auto-installation Program\""; my $error = system "$exe -s $subject $mail < $log"; print " WARNING: Problem sending message with $exe\n\n" if $error; system "rm -f $log" unless $error; return; } # ----------------------------------------------------------------------- # sub help_menu { # STRC WRF Update scrpt help menu # print "\n\n NWS SOO/STRC WRF EMS package update script\n\n"; print " % $Option{EXE} [Options]\n\n"; print " Where the option list consists of the following:\n\n"; print " Option Description\n\n"; print " --update Download available WRF EMS updates from the ftp server\n", " --cumulative Download available cumulative WRF EMS updates from the ftp server\n", " --[no]install Do [not] install updates. Just let them sit on the local machine.\n\n", " --list List the updates needed to bring WRF EMS release up to current levels\n", " --listall List all the updates available on the ftp server\n\n", " --force Just install the update even though it was previously installed\n", " --[no]delete Do [not] delete the updates after installation\n", " --restore Restore your distribution to release prior to last update\n", " --[no]uconf Do [not] update the user domain configuration files\n", " --clean Clean WRF EMS distribution of previously updated files (.prev)\n", " --nomail Don't send mail to the addresses defined in MAILTO\n\n", " --version Just print out the current version\n", " --verbose Let me know everything that is going on, even the stuff I don't want to know\n", " --help For additional help, love, understanding, and this menu again.\n\n\n"; print " Additional Hand-holding\n\n"; print " The primary purpose of this script is to provide users with an easy way of updating their\n", " STRC WRF EMS distribution. The configuration file can be found in:\n\n", " $ENV{WRF}/data/conf/config/wrf_update/wrf_update.conf\n\n", " This script may be run from the command line or placed in cron; however, it is recommended\n", " that when running from a cron that you set the MAILTO option in the configuration file.\n\n"; print " Note that all command line options override those in the configuration file\n\n"; print " A Few Examples:\n\n"; print " % $Option{EXE} --listall\n\n", " List all the available updates on the SOO/STRC ftp server. No download or installation.\n\n", " % $Option{EXE} --list\n\n", " List the updates necessary to bring my release to current levels.No download or installation.\n\n", " % $Option{EXE} --update --install\n\n", " Download the necessary updates from the SOO/STRC ftp server and install. Note that --install\n", " is not necessary if INSTALL = Yes in the configuration file.\n\n", " % $Option{EXE} --update --noinstall \n\n", " Download the necessary updates from the SOO/STRC ftp server but do not install.\n\n", " % $Option{EXE} --cumulative --install\n\n", " Download and install all the cumulative updates I need to bring my WRF EMS distribution\n", " up-to-date. I've been slow and lazy in updating my system so give me all you\'ve got even\n", " though I may be updating files that I've already updated. Consequences-Shmonsequences.\n\n", " % $Option{EXE} --cumulative --install --force\n\n", " Download and install all the cumulative updates even though some have already been installed.\n", " I'm in a surly mood and I don't want to be second guessed by a computer even if it's usually\n", " correct.\n\n", " % $Option{EXE} --restore\n\n", " I don't like what I see from my update install and I want to go back to the previous\n", " EMS release. Besides, I was just getting comfortable with it, just like my favorite\n", " pair of socks|shoes|jeans|underwear before the first hole appeared.\n\n\n"; exit; }