--- loncom/build/check-rpms 2002/02/24 22:33:45 1.1 +++ loncom/build/check-rpms 2002/09/05 17:20:46 1.5 @@ -1,8 +1,67 @@ #!/usr/bin/perl # -# check-rpms, version 2.1.0 +# check-rpms, version 2.1.1 # Martin Siegert, SFU, siegert@sfu.ca, Feb 02 # +# documentation and minor patches, +# Scott Harrison sharrison@users.sourceforge.net 2002 + +=pod + +=head1 NAME + +check-rpms - compare installed rpms with up-to-date distribution + +=head1 DESCRIPTION + +I compares installed RPM packages (listed by the command +"rpm -qa") on a Linux system with an up-to-date distribution. That +distribution may either reside in a local directory (possibly NFS +mounted) or on a ftp server. If the B<-ftp> option is specified, +I retrieves directory listings from the I's +I/ directories, where is set to noarch, i386, +i586, i686, and athlon consecutively. If I is +not specified, $FTPSERVER/$FTPUPDATES is used. The $FTPSERVER and +$FTPUPDATES variables can be set in the configuration file. If +either of the two is not set, the default server "updates.redhat.com" +and the default directory "$RHversion/en/os" is used, +where $RHversion is obtained from the /etc/redhat-release file. If +run with the B<-ftp> option, all rpm packages that need to be downloaded +(see the B<--download>, B<--recheck>, and B<--update> options) will +be downloaded into the directory specified by the B<-d> directory +option. If that option is omitted the $RPMDIR directory is used. +The $RPMDIR variable that can be set in the configuration file. If +$RPMDIR variable is not set either, the default directory +"/mnt/redhat/RedHat/RPMS" is used. + +If the B<-ftp> is omitted, it is assumed that B<-d> I specifies +a local directory that contains up-to-date rpm packages. If B<-d> +I is omitted as well, the $RPMDIR directory is used. If +$RPMDIR is not set, the default directory "/mnt/redhat/Red- +Hat/RPMS" is used. + +I uses a lexical sort on the version string and the +release string of the package in order to decide whether the +installed package or the package form the distribution is newer. +I lists packages of the distribution that are found to be +newer than the installed packages or, if B<--update> is specified, +will update the packages using the "rpm -Fvh " +command. In the latter case I must be run as root. Fur- +thermore, the $RPMUSER variable should be set to a non-root user- +name (see the B<-c> option below). I will switch to that +user and run most of the script under that user id.Only the +final "rpm -Fvh ..." command will be run as root. If $RPMUSER is +not set, the "nobody" user id will be used. It is recommended to +set $RPMUSER to an ordinary username (such as yourself). Further- +more, if a ftp server is used, create the download directory +(which is specified in the B<-d> directory option or in the $RPMDIR +variable), change the owner ship of that directory to that user, +and set the permissions to 700 before running I with the +B<--update> option. Note, that B<--update> implies the B<--no-kernel> +option, i.e., I refuses to update the kernel directly. + +=cut + # ************ WARNING ***************************************************** # THIS PROGRAM IS PROVIDED "AS IS" WITHOUT # WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLICIT. @@ -31,7 +90,167 @@ use Getopt::Long; my $retval = &GetOptions("verbose|v","lm|list-missing","lq|list-questionable", "dir|d=s","ftp:s","noftp","download|dl","recheck|r", - "nk|no-kernel","update","c=s"); + "nk|no-kernel","update","c=s","rpmuser=s"); + +=pod + +=head1 OPTIONS + +=over 4 + +=item B<-v> B<--verbose> + +verbose mode: prints additional progress information on +standard output + +=item B<-ftp> [I] + +compare the installed packages with the rpm packages found +on the ftp server I in the directories I/, +where arch is set to noarch, i386, i586, i686, +and athlon consecutively. If I is not +specified, the $FTPSERVER and $FTPUPDATES variables are +checked. These variables can be set in the configuration +file (see the B<-c> option below). If those variables are not +set either, the default server "updates.redhat.com" and the +default directory "$RHversion/en/os" is used, where $RHversion +is obtained from the I file. + +=item B<-noftp> + +use a local directory as the source for new rpm packages +even if the $FTP veriable is set to 1 in the configuration +file. + +=item B<-d> I B<--rpm-directory> I + +if B<-ftp> is specified download all rpm packages that need to +be downloaded into I. If B<-ftp> is not specified, +regard the rpm packages found in I as an up-to-date +distribution against which the installed packages are +compared to. + +=item B<-lm> B<--list-missing> + +list installed packages that do not have an equivalent in +the up-to-date distribution. This will generate lots of +output when the comparison is made with the updates directory +of a ftp server. + +=item B<-lq> B<--list-questionable> + +list packages for which the lexical sort algorithm does not +give a conclusive result on whether the installed package +is older than the package in the distribution. These are +packages that have version and/or release strings that contain +letters. For example, it is not absolutely clear +whether the version 1.2.3b is actually newer or older than +1.2.3. The lexical sort would classify 1.2.3b to be newer +than 1.2.3; with B<-lq> specified the package would be listed +in any case. See also B<--recheck> below. + +=item B<-dl> B<--download> + +download packages from the remote ftp server that are found +to be newer than installed packages into the directory that +is specified in the B<-d> I option or in the $RPMDIR +variable or, if neither of the two are specified, into +"/mnt/redhat/RedHat/RPMS". If the download directory does +not exist, I will create it. + +=item B<-r> B<--recheck> + +Use the "rpm -Uvh --test --nodeps " command to +check all packages that have letters in their version +and/or release string; B<--recheck> implies B<--list-questionable> +(see above). At the time of writing (Feb. 2002) there +is one known case for which the lexical sort algorithm +fails to detect a new package: mutt-1.2.5.1 was released to +replace mutt-1.2.5i, however, the lexical sort algorithm +incorrectly classifies mutt-1.2.5i to be newer than +mutt-1.2.5.1. In this case using the B<--recheck> option is +essential. In all other cases it is not. It is nevertheless +probably a good idea to use B<--recheck> at least once in a +while. B<--recheck> can increase the run-time of I +substantially, particularly if a ftp server is used. In +that case the questionable packages must be downloaded from +the server into a directory I (as specified in the +-d option or the $RPMDIR variable) which will be created, +if it does not exist. + +=item B<-nk> B<--no-kernel> + +do not list kernel packages. That is, kernel, kernel-smp, +kernel-enterprise, kernel-BOOT, and kernel-debug will not +be checked and listed. However, kernel-headers and kernel-source +will be checked. The B<--update> option (see below) +implies B<--no-kernel>. + +=item B<--update> + +update all packages that were found to have newer versions. +For this to work I must be run as root and a suitable +$RPMUSER must exist (see DESCRIPTION above). It is +strongly advisable to do a dry run B before +running B. + +=item B<-c> I + +The optional configuration file to use. This file can be +used to specify the $RPMDIR variable, the $FTP, $FTPSERVER, +and $FTPUPDATES, variables, and the $RPMUSER variable. An +example configuration file is given below. If the B<-c> option +is omitted, I will use the default configuration +file I, if it exists. + +=item B<--rpmuser> I + +Specifying $RPMUSER on the command line. + +=back + +=head1 EXAMPLES + +=over 4 + +=item check-rpms + +will 1) check whether /usr/local/etc/check-rpms.conf exists; 2) if +it does it will read the variables specified in that file, if it +doesn't exist, $RPMDIR is set to /mnt/redhat/RedHat/RPMS; 3) if +$RPMDIR is set, this directory will be regarded as the source of +the up-to-date distribution, unless $FTP is set to 1. In that latter +case the $FTPSERVER and $FTPUPDATES are used, if those variables are +set. Otherwise "updates.redhat.com" and "/en/os" +will be used; 4) the installed packages are compared + +=item check-rpms -v -lq -d /mnt/redhat/7.1/RedHat/RPMS + +will use the distribution in the directory /mnt/redhat/7.1/RedHat/RPMS +for comparison with the installed packages. The command +will give more detailed information on its progress and will list +the packages that need upgrading and in another section it will +list packages they may need to be upgraded. + +=item check-rpms -v -lq -ftp updates.redhat.com/7.1/en/os + +same as above, but the directories 7.1/en/os/noarch, +7.1/en/os/i386, 7.1/en/os/i586, 7.1/en/os/i686, and +7.1/en/os/athlon on updates.redhat.com will be searched for new +packages. + +=item check-rpms -v -r --updates + +will use the default location for updated packages (determined as +indicated in the first example); if a ftp server is used, it will +download all newer and all packages with letters in the version +and/or release strings (i.e., "questionable" packages) from that +ftp server, recheck the questionable packages, and finally update +all packages that need to be updated. + +=back + +=cut if ( $retval == 0 ) { usage(); @@ -61,6 +280,41 @@ if ($opt_c) { $CONF = $DEFCONF; } +=pod + +=head1 The Configuration File + +All variables must be defined using perl syntax, i.e., in the form + +$variable = value; + +(do not forget the semicolon at the end of a line). Comments +start with "#" and blank lines may be included as well. + +Example configuration file: + + # check-rpms configuration file + + # $RPMDIR is the directory where up-to-date RPMs can be found and/or + # rpm packages are downloaded into. + $RPMDIR = "/mnt/redhat/RedHat/RPMS"; + + # $RPMUSER is the user name that check-rpms switches to for most of + # the script when run as root + $RPMUSER = "adminjoe"; + + # $FTPSERVER and $FTPUPDATES are the hostname of a ftp server and the + # directory where RPM updates can be found without the directory. + # I.e., $FTPUPDATES should be set to something like pub/7.2, if the RPMs + # are located in pub/7.2/i386, pub/7.2/i686, etc. + # $FTPSERVER and $FTPUPDATES are used if -ftp is specified or if the following + # line is uncommented. + # $FTP = 1; + $FTPSERVER = "updates.redhat.com"; + $FTPUPDATES = "7.2/en/os"; + +=cut + if ( -f $CONF) { require($CONF); } else { @@ -68,6 +322,10 @@ if ( -f $CONF) { $FTPUPDATES = $DEFFTPUPDATES; } +if ($opt_rpmuser) { + $DEFRPMUSER = $opt_rpmuser; +} + # check whether we are running as root if ($< == 0){ if (! $RPMUSER) { @@ -124,7 +382,12 @@ if (defined $opt_ftp || $FTP) { if ($download || $recheck) { if ( ! -d $RPMDIR) { - $retval = system("mkdir -p $RPMDIR; chmod 700 $RPMDIR"); + if ($verbose) { print "Creating $RPMDIR ...\n"; } + if ($< == 0) { + $retval = system("su $RPMUSER -c \'mkdir -p $RPMDIR\'; chmod 700 $RPMDIR"); + } else { + $retval = system("mkdir -p $RPMDIR; chmod 700 $RPMDIR"); + } if ($retval) { die "error: could not create $RPMDIR\n"; } } } @@ -140,7 +403,7 @@ if (defined $opt_update || defined $opt_ $no_kernel=1; } -$PROC = `grep -i athlon /proc/cpuinfo`; +$PROC = `grep -i "athlon\|amd" /proc/cpuinfo`; if ( ! "$PROC" ) { $PROC = `uname -m`; chomp($PROC); @@ -211,6 +474,7 @@ if ($ftp) { $giveup = 0; for (@templist) { ($rpm, $pkg, $pver, $arch) = m/(([^ ]*)-([^- ]+-[^-]+\.(\w+)\.rpm))/; + if (! defined $local_rpm{$pkg}) { next; } if ($remote_rpm{$pkg}) { # problem: there are several versions of the same package. # this means that the package exists for different architectures @@ -293,11 +557,7 @@ for (@local_rpm_list) { push(@q_updates, $rpm); } elsif ( $vcmp < 0 ) { # local version is lower - if ( $qflag ) { - push(@q_updates, $rpm); - } else { - push(@updates, $rpm); - } + push(@updates, $rpm); } } elsif ($list_missing) { print "Package '$pkg' missing from remote repository\n"; @@ -451,7 +711,25 @@ sub pkg_compare($$$) { } my $serial1 = `rpm -qp --queryformat '%{SERIAL}' $RPMDIR/$pkg1`; my $serial2 = `rpm -qp --queryformat '%{SERIAL}' $RPMDIR/$pkg2`; - ($serial2 > $serial1) ? return 1 : return 0; + if ($serial2 > $serial1) { + remove_pkg("$RPMDIR/$pkg1"); + return 1; + } else { + remove_pkg("$RPMDIR/$pkg2"); + return 0; + } +} + +sub remove_pkg($) { + my ($pkg) = @_; + if ($verbose) { + print "Removing $pkg ...\n"; + } + my $status = system("rm -f $pkg"); + if ($status) { + printf STDERR "error: could not remove $pkg. You must remove this file before updating.\n"; + if ($update) { $giveup = 1; } + } } sub mulpkg_msg($$$) { @@ -548,3 +826,15 @@ sub usage(){ " [-lq | --list-questionable] [-r | --recheck ]\n", " [-nk | --no-kernel] [--update] [-c configurationfile]\n"; } + +=pod + +=head1 SEE ALSO + +rpm(8), ncftpls(1), ncftpget(1) + +=head1 AUTHOR + +Martin Siegert, Simon Fraser University, siegert@sfu.ca + +=cut