--- loncom/build/loncaparestoreconfigurations 2000/11/02 17:36:56 1.7 +++ loncom/build/loncaparestoreconfigurations 2003/02/03 18:03:52 1.16 @@ -1,57 +1,299 @@ #!/usr/bin/perl -# loncaparestoreconfigurations +# loncaparestoreconfigurations - restore data to new LON-CAPA conf files +# +# $Id: loncaparestoreconfigurations,v 1.16 2003/02/03 18:03:52 harris41 Exp $ +# +### -# This tool helps in updating a system. It takes a list of -# .rpmsave files and restores them. +# This tool helps in updating a system. It restores information for +# configuration files (.lpmlnew or other backup notations). -# Scott Harrison, 10/25/2000 +# By default, the .lpmlsave suffix is used. +# Alternatively, there can be two other invocations +# Invocation #1: +# ARGV[0]=suffix +# ARGV[1]=.bak +# Invocation #2: +# ARGV[0]=lasttimestamp -use strict; +# The criteria for the lasttimestamp is that the +# file suffix is a '.' followed by a 14-digit +# time-stamp (YYYYMMDDhhmmss). +# The time-stamp with the greatest value is +# taken as the backup file. +# --------------------------------------------- Define program version variable +$VERSION = sprintf("%d.%02d", q$Revision: 1.16 $ =~ /(\d+)\.(\d+)/); + +# ---------------------------------------------- Process command-line arguments +my $suffix='.lpmlsave'; +my $suffixpragma=''; +if ($ARGV[0] eq 'suffix') { + $suffix=$ARGV[1] if $ARGV[1]=~/^[\.\w]+$/; +} +elsif ($ARGV[0] eq 'lasttimestamp') { + $suffixpragma='lasttimestamp'; +} + +use strict; # restrict unsafe and poorly coded constructs + +# ------------------------------------ Configuration files to be concerned with my @special_conf_files=( - "/etc/httpd/conf/access.conf", - "/etc/smb.conf" + '/etc/httpd/conf/loncapa.conf', ); -my @generic_conf_files=( - "/home/httpd/lonTabs/hosts.tab", - "/home/httpd/lonTabs/spare.tab", - "/etc/krb.conf", - "/etc/ntp.conf", - ); +my %pvar; # store the PerlSetVar variable key/value combinations -my @perlsetvars=("lonHostID","lonRole","lonAdmEMail","lonDefDomain","lonLoadLim","lonExpire"); -my %pvar; -foreach (@special_conf_files) { - if (/^\/etc\/httpd\/conf\/access.conf$/) { - my $template=`/bin/cat /etc/httpd/conf/access.conf`; - my $rpmsave=`/bin/cat /etc/httpd/conf/access.conf.rpmsave`; - `/bin/mv /etc/httpd/conf/access.conf /etc/httpd/conf/access.conf.template`; - foreach my $psv (@perlsetvars) { - $rpmsave=~/\nPerlSetVar\s+$psv\s+(\S+)/; - my $pval=$1; - $template=~s/(\nPerlSetVar\s+$psv\s+)\S+/$1$pval/; - $pvar{$psv}=$pval; - } - open OUT,">/etc/httpd/conf/access.conf"; - print OUT $template; - close OUT; - } - if (/^\/etc\/smb.conf$/) { - my $template=`/bin/cat /etc/smb.conf`; - foreach my $psv (@perlsetvars) { - $template=~s/\{\{\{\{\[(.*?)\]\}\}\}\}/$pvar{$1}/ge; - } - open OUT,">/etc/smb.conf"; - print OUT $template; - close OUT; - } -} +# --------------------------------------------- Process the configuration files +# NOTE that I have structured this processing to make NO assumptions +# about the processing of each configuration file. So, in terms +# of keeping each file's processing algorithms self-contained, I am not +# modularizing things (where it is obvious that they might be modularized.) +CONFLOOP: foreach (@special_conf_files) { -foreach (@generic_conf_files) { - if (-e "$_.rpmsave") { - `/bin/mv $_ $_.template`; - `/bin/mv $_.rpmsave $_`; - } -} + my $lpmlold; # holds information that needs to be read + my $lpmlnew; # holds information that needs to be modified + + my $lpmlnew_file; # file location of information that needs to be modified + +# ---------------------------------------------------------------- loncapa.conf + if (/^\/etc\/httpd\/conf\/loncapa.conf$/ and + -e '/etc/httpd/conf/loncapa.conf') + { + if ($suffixpragma eq 'lasttimestamp' and + -e '/etc/httpd/conf/loncapa.conf') + { + $suffix=&getsuffix('/etc/httpd/conf/loncapa.conf'); + unless (-e '/etc/httpd/conf/loncapa.conf'.$suffix) + { + next CONFLOOP; + } + $lpmlold="\n".&readfile('/etc/httpd/conf/loncapa.conf'.$suffix); + $lpmlnew_file='/etc/httpd/conf/loncapa.conf'; + $lpmlnew=&readfile($lpmlnew_file); + } + else + { + $lpmlold="\n".&readfile('/etc/httpd/conf/loncapa.conf'); + $lpmlnew_file='/etc/httpd/conf/loncapa.conf'.$suffix; + unless (-e $lpmlnew_file) + { + next CONFLOOP; + } + $lpmlnew=&readfile($lpmlnew_file); + } + while($lpmlold=~/\n\s*PerlSetVar\s+(\S+)\s+(\S+)/mcg) + { + my $pkey=$1; my $pval=$2; + $pvar{$pkey}=$pval; + } + foreach my $pkey (keys %pvar) + { + my $pval=$pvar{$pkey}; + $lpmlnew=~s/(\n\s*PerlSetVar\s+$pkey\s+)\S+/$1$pval/; + } + open(OUT,'>'.$lpmlnew_file) or + die('Cannot open '.$lpmlnew_file.' for output'."\n"); + print(OUT $lpmlnew); + close(OUT); + } + +# ---------------------- smb.conf (probably will be deprecated in 2004 or 2005) + elsif (/^\/etc\/smb.conf$/ and -e "/etc/smb.conf$suffix") + { + if ($suffixpragma eq 'lasttimestamp') + { + $suffix=&getsuffix('/etc/smb.conf'); + unless (-e '/etc/httpd/conf/loncapa.conf'.$suffix) + { + next CONFLOOP; + } + $lpmlnew = &readfile('/etc/smb.conf'); + $lpmlnew_file = '/etc/smb.conf'; + } + else + { + $lpmlnew = &readfile('/etc/smb.conf'.$suffix); + $lpmlnew_file = '/etc/smb.conf'.$suffix; + } + $lpmlnew =~ s/\{\{\{\{\[(.*?)\]\}\}\}\}/$pvar{$1}/ge; + open(OUT,'>'.$lpmlnew_file) or + die('Cannot open '.$lpmlnew_file.' for output'."\n"); + print(OUT $lpmlnew); + close(OUT); + } + elsif (/^\/etc\/samba\/smb.conf$/ and -e "/etc/samba/smb.conf$suffix") + { + if ($suffixpragma eq 'lasttimestamp') + { + $suffix = &getsuffix('/etc/samba/smb.conf'); + unless (-e '/etc/samba/smb.conf'.$suffix) + { + next CONFLOOP; + } + $lpmlnew = &readfile('/etc/samba/smb.conf'); + $lpmlnew_file = '/etc/samba/smb.conf'; + } + else + { + $lpmlnew = &readfile('/etc/samba/smb.conf'.$suffix); + $lpmlnew_file = '/etc/samba/smb.conf'.$suffix; + } + $lpmlnew =~ s/\{\{\{\{\[(.*?)\]\}\}\}\}/$pvar{$1}/ge; + open(OUT,'>'.$lpmlnew_file) or + die('Cannot open '.$lpmlnew_file.' for output'."\n"); + print(OUT $lpmlnew); + close(OUT); + } + } + +# --------------------------------- getsuffix: get the latest time stamp suffix +# === INPUT: filename without suffix +# === OUTPUT: the latest time stamp suffix; 14 digits YYYYMMDDhhmmss +# === ERROR: cannot read the directory in which the filenames reside +sub getsuffix ($) + { + my ($file) = @_; + print("$file\n"); + my $dir = $file; $dir =~ s/([^\/]+)$//; + my $filename = $1; + opendir(DIR,$dir) or + die('Cannot open directory '.$dir.' for viewing'."\n"); + my @a = grep {/$filename\.\d{14}/} readdir(DIR); + closedir(DIR); + map {s/$filename\.//;} @a; + my @b = sort {$a<=>$b} @a; + my $suffix = '.'.$b[$#b]; + return($suffix); + } + +# -------------------------- readfile: get the file contents in a scalar string +# === INPUT: filename +# === OUTPUT: the filename's contents +# === ERROR: cannot read the file +# === NOTE: big files will hog computer memory +sub readfile ($) + { + my ($filename) = @_; + my $contents = ''; + open(IN,'<'.$filename) or die ('Cannot read '.$filename."\n"); + while() {$contents .= $_;} + close(IN); + return($contents); + } + +=pod + +=head1 NAME + +B - restore data to new LON-CAPA conf files + +=head1 SYNOPSIS + + perl loncaparestoreconfigurations suffix .lpmlnew + +=head1 DESCRIPTION + +During software upgrades, it is possible that configuration files will change. +It is important to "intelligently" preserve the machine-specific configuration +data. This script is meant to run B the software upgrade. + +For example, consider the configuration file F. +During the software upgrade (not performed by by F), +the following happens: + + loncapa.conf is NOT overwritten + +rather, + + a NEW file B is GENERATED + (cp UPGRADEDIR/loncapa.conf SYSTEMDIR/loncapa.conf.lpmlnew) + +This script can be described as: + +=over 4 + +=item * + +modifying SYSTEMDIR/loncapa.conf.lpmlnew, and + +=item * + +the modification consists of reading values from the old loncapa.conf and +placing them in loncapa.conf.lpmlnew. + +=back + +Regarding F, for backwards compatibility, this script tries +to read values out of F. + +This script also currently works with F (a standard Linux +configuration file associated with sharing the Linux filesystem with +Windows machines). + +=head2 Working with the file suffix + +The script is designed to work according to two strategies. + +=over 4 + +=item * B + +In the aggressive update strategy, two things should happen: + +=over 4 + +=item * The configuration file should be replaced + +Therefore, the system administrator "trusts" the software update process +and this script to handle everything correctly. + +=item * Information should never be lost + +Therefore, a backup copy should be made that is unique to the time +the action is taken and is never overwritten or destroyed by the +automated process. + +=back + +=item * B + +=over 4 + +=item * The configuration file should not be replaced + +The system administrator does not trust the software update process. +She would rather have a new file "intelligently" generated, and, only +by her direct approval, have the new file substitute the contents +of the current configuration file. + +=item * The script should try to help the system administrator + +Therefore, a new copy is made with the suffix ".lpmlnew". This +new copy is modified with data from the existing configuration file. +The system administrator is prompted (by the rest of the software +upgrade process) to resolve the new changes to the configuration +file. + +=back + +=back + +Correspondingly, + + perl loncaparestoreconfigurations suffix .lpmlnew + +invokes this script in B mode; whereas + + perl loncaparestoreconfigurations lasttimestamp + +invokes this script in B mode. + +=head1 AUTHORS + + +This script is free software; you can redistribute it +and/or modify it under the same terms as LON-CAPA itself. + +=cut