version 1.1, 2002/02/24 22:33:45
|
version 1.5, 2002/09/05 17:20:46
|
Line 1
|
Line 1
|
#!/usr/bin/perl |
#!/usr/bin/perl |
# |
# |
# check-rpms, version 2.1.0 |
# check-rpms, version 2.1.1 |
# Martin Siegert, SFU, siegert@sfu.ca, Feb 02 |
# 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<check-rpms> 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<check-rpms> retrieves directory listings from the I<ftpserver>'s |
|
I<directory>/<arch> directories, where <arch> is set to noarch, i386, |
|
i586, i686, and athlon consecutively. If I<ftpserver/directory> 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<directory> specifies |
|
a local directory that contains up-to-date rpm packages. If B<-d> |
|
I<directory> 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<check-rpms> 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<check-rpms> 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 <list of packages>" |
|
command. In the latter case I<check-rpms> 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<check-rpms> 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<check-rpms> with the |
|
B<--update> option. Note, that B<--update> implies the B<--no-kernel> |
|
option, i.e., I<check-rpms> refuses to update the kernel directly. |
|
|
|
=cut |
|
|
# ************ WARNING ***************************************************** |
# ************ WARNING ***************************************************** |
# THIS PROGRAM IS PROVIDED "AS IS" WITHOUT |
# THIS PROGRAM IS PROVIDED "AS IS" WITHOUT |
# WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLICIT. |
# WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLICIT. |
Line 31 use Getopt::Long;
|
Line 90 use Getopt::Long;
|
|
|
my $retval = &GetOptions("verbose|v","lm|list-missing","lq|list-questionable", |
my $retval = &GetOptions("verbose|v","lm|list-missing","lq|list-questionable", |
"dir|d=s","ftp:s","noftp","download|dl","recheck|r", |
"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<ftpserver/directory>] |
|
|
|
compare the installed packages with the rpm packages found |
|
on the ftp server I<ftpserver> in the directories I<directory>/<arch>, |
|
where arch is set to noarch, i386, i586, i686, |
|
and athlon consecutively. If I<ftpserver/directory> 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</etc/redhat-release> 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<directory> B<--rpm-directory> I<directory> |
|
|
|
if B<-ftp> is specified download all rpm packages that need to |
|
be downloaded into I<directory>. If B<-ftp> is not specified, |
|
regard the rpm packages found in I<directory> 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<directory> 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<check-rpms> will create it. |
|
|
|
=item B<-r> B<--recheck> |
|
|
|
Use the "rpm -Uvh --test --nodeps <package>" 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<check-rpms> |
|
substantially, particularly if a ftp server is used. In |
|
that case the questionable packages must be downloaded from |
|
the server into a directory I<directory> (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<check-rpms> must be run as root and a suitable |
|
$RPMUSER must exist (see DESCRIPTION above). It is |
|
strongly advisable to do a dry run B<check-rpms -v -lq> before |
|
running B<check-rpms --update>. |
|
|
|
=item B<-c> I<configurationfile> |
|
|
|
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<check-rpms> will use the default configuration |
|
file I</usr/local/etc/check-rpms.conf>, if it exists. |
|
|
|
=item B<--rpmuser> I<user name> |
|
|
|
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 "<RHversion>/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 ) { |
if ( $retval == 0 ) { |
usage(); |
usage(); |
Line 61 if ($opt_c) {
|
Line 280 if ($opt_c) {
|
$CONF = $DEFCONF; |
$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 <arch> 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) { |
if ( -f $CONF) { |
require($CONF); |
require($CONF); |
} else { |
} else { |
Line 68 if ( -f $CONF) {
|
Line 322 if ( -f $CONF) {
|
$FTPUPDATES = $DEFFTPUPDATES; |
$FTPUPDATES = $DEFFTPUPDATES; |
} |
} |
|
|
|
if ($opt_rpmuser) { |
|
$DEFRPMUSER = $opt_rpmuser; |
|
} |
|
|
# check whether we are running as root |
# check whether we are running as root |
if ($< == 0){ |
if ($< == 0){ |
if (! $RPMUSER) { |
if (! $RPMUSER) { |
Line 124 if (defined $opt_ftp || $FTP) {
|
Line 382 if (defined $opt_ftp || $FTP) {
|
|
|
if ($download || $recheck) { |
if ($download || $recheck) { |
if ( ! -d $RPMDIR) { |
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"; } |
if ($retval) { die "error: could not create $RPMDIR\n"; } |
} |
} |
} |
} |
Line 140 if (defined $opt_update || defined $opt_
|
Line 403 if (defined $opt_update || defined $opt_
|
$no_kernel=1; |
$no_kernel=1; |
} |
} |
|
|
$PROC = `grep -i athlon /proc/cpuinfo`; |
$PROC = `grep -i "athlon\|amd" /proc/cpuinfo`; |
if ( ! "$PROC" ) { |
if ( ! "$PROC" ) { |
$PROC = `uname -m`; |
$PROC = `uname -m`; |
chomp($PROC); |
chomp($PROC); |
Line 211 if ($ftp) {
|
Line 474 if ($ftp) {
|
$giveup = 0; |
$giveup = 0; |
for (@templist) { |
for (@templist) { |
($rpm, $pkg, $pver, $arch) = m/(([^ ]*)-([^- ]+-[^-]+\.(\w+)\.rpm))/; |
($rpm, $pkg, $pver, $arch) = m/(([^ ]*)-([^- ]+-[^-]+\.(\w+)\.rpm))/; |
|
if (! defined $local_rpm{$pkg}) { next; } |
if ($remote_rpm{$pkg}) { |
if ($remote_rpm{$pkg}) { |
# problem: there are several versions of the same package. |
# problem: there are several versions of the same package. |
# this means that the package exists for different architectures |
# this means that the package exists for different architectures |
Line 293 for (@local_rpm_list) {
|
Line 557 for (@local_rpm_list) {
|
push(@q_updates, $rpm); |
push(@q_updates, $rpm); |
} elsif ( $vcmp < 0 ) { |
} elsif ( $vcmp < 0 ) { |
# local version is lower |
# local version is lower |
if ( $qflag ) { |
push(@updates, $rpm); |
push(@q_updates, $rpm); |
|
} else { |
|
push(@updates, $rpm); |
|
} |
|
} |
} |
} elsif ($list_missing) { |
} elsif ($list_missing) { |
print "Package '$pkg' missing from remote repository\n"; |
print "Package '$pkg' missing from remote repository\n"; |
Line 451 sub pkg_compare($$$) {
|
Line 711 sub pkg_compare($$$) {
|
} |
} |
my $serial1 = `rpm -qp --queryformat '%{SERIAL}' $RPMDIR/$pkg1`; |
my $serial1 = `rpm -qp --queryformat '%{SERIAL}' $RPMDIR/$pkg1`; |
my $serial2 = `rpm -qp --queryformat '%{SERIAL}' $RPMDIR/$pkg2`; |
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($$$) { |
sub mulpkg_msg($$$) { |
Line 548 sub usage(){
|
Line 826 sub usage(){
|
" [-lq | --list-questionable] [-r | --recheck ]\n", |
" [-lq | --list-questionable] [-r | --recheck ]\n", |
" [-nk | --no-kernel] [--update] [-c configurationfile]\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 |