#!/usr/bin/perl -w # Nastiness to automatically update a redhat box from assorted update sites. # Then I discovered rpmfind (http://www.rpmfind.net/) # Waider 1999 # # 2002 rehack # * discovered that the RPM package has a neat version comparison tool # * discovered that it doesn't appear to work any better than what I'd # written myself. Ptui. # # 09/05/2003 Merging all the versions # 30/05/2003 Use home-made ftp->get mechanism # 08/06/2003 RPM.pm is defunct. RPM2 appears to be The Good Shit. use Net::FTP; use strict; my $RPMAPI = "RPM"; eval { require RPM; import RPM "vercmp"; }; if ( $@ ) { eval { require RPM2; my $RPMAPI = "RPM2"; }; if ( $@ ) { die "Can't find a usable RPM module\n"; } } $| = 1; my $ftp; my $verbose = ( $#ARGV > -1 && $ARGV[0] eq "-v" ); my ( $HOST, $DIR ); my ( $USER, $PASS ) = ( 'anonymous', 'waider@waider.ie' ); sub maybe_fetch_file( $ ) { my $file = shift; my ( $locsz, $loctm, $remsz, $remtm ); # check for local copy of the file if ( -r $file ) { # stat local copy, get mtime & size (undef, undef, undef, undef, undef, undef, undef, $locsz, undef, $loctm, undef, undef, undef ) = stat( $file ); } else { $locsz = 0; $loctm = 0; } # check if ftp connection is live, reopen if necessary if ( !defined( $ftp ) || !ref( $ftp ) || !$ftp->pwd ) { $ftp = Net::FTP->new( $HOST, Debug=>$verbose, Hash => \*STDERR ); $ftp->login( $USER, $PASS ); $ftp->cwd( $DIR ); $ftp->pasv(); $ftp->binary(); } # FIXME check that file exists on server! # check mtime & size of remote copy $remsz = $ftp->size( $file ) or die $!; # if mtime of local is more recent & sizes match, return if ( $remsz == $locsz ) { # $remtm = $ftp->mdtm( $file ); # if ( $remtm <= $loctm ) { # return 1; # } } # fetch file # try to recover from partial fetch if ( $locsz ) { print " recovering..."; ftp_get( $file, $remsz ) or die $!; } else { print " fetching..."; ftp_get( $file, $remsz ) or die $!; } 1; } # Get rpm list print "Fetching list of installed RPMS\n" if $verbose; open( RPMLIST, "rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n'|") or die $!; my @installed = ; close( RPMLIST ); my %installed; # Sort it into package, version print "Sorting list of installed RPMS\n" if $verbose; for my $rpm ( @installed ) { my ( $pkg, $version ) = split( /\s/, $rpm ); $installed{$pkg} = $version; } opendir( HERE, "." ); my @downloads = readdir( HERE ); closedir( HERE ); # figure out what directory we need print "Checking system version..." if $verbose; open( FOO, "; close( FOO ); chomp( $os ); $os =~ s/^.* (\d+\.\d+) .*$/$1/; print "$os\n" if $verbose; #$HOST = "ftp.rackspace.com"; $HOST = "ftp.esat.net"; $ftp = Net::FTP->new( $HOST, Debug => $verbose ) || die "Could not connect to $HOST: $!"; $ftp->login( $USER, $PASS ) || die "ftp error: $!"; for my $dir ( "/mirrors/ftp.redhat.com/redhat/linux/$os/en/os/i386/RedHat/RPMS", "/mirrors/updates.redhat.com/$os/en/os/i386", "/mirrors/updates.redhat.com/$os/en/os/i686", "/mirrors/updates.redhat.com/$os/en/os/noarch" ) { print "Checking $dir\n" if $verbose; $ftp->cwd( "$dir" ); $DIR = "$dir"; my @available = $ftp->ls; for my $rpm ( @available ) { next unless $rpm =~ /\.rpm$/i; my ( $pkg, $version, undef ) = $rpm =~ m/^(.*)-([a-z0-9._+]+\-.*)\.(i[3-6]86|noarch|athlon)?(-glibc\d+)?\.rpm$/i; if ( !defined( $pkg ) || !defined( $version )) { warn "Tweak regexp to also match $rpm.\n"; } print "$pkg version $version: " if $verbose; # $installed{$pkg} = "0-0" unless $installed{$pkg}; if ( defined( $installed{$pkg})) { if ( $verbose ) { print " (installed is $installed{$pkg})"; } # Need to split version and revision my ( $v1, $r1 ) = split( /-/, $version, 2 ); my ( $v2, $r2 ) = split( /-/, $installed{$pkg}, 2 ); my $upg = 0; if ( vercmp( $v1, $r1, $v2, $r2 ) > 0 ) { $upg = 1; } if ( $upg ) { print "$pkg $version (installed is $installed{$pkg}" unless $verbose; print " (upgradable)"; print "\n" unless $verbose; # fetch files that we can upgrade to maybe_fetch_file( $rpm ) unless -f $rpm; } } print "\n" if $verbose; } } $ftp->close() if defined( $ftp ); sub ftp_get { my ( $file, $expected ) = @_; my ( $nr, $nw, $buf, $tot ); $ftp->binary(); my $dataconn = $ftp->retr( $file ) or die $!; open( FILE, ">$file" ) or die; while ( $nr = $dataconn->read( $buf, 1024 )) { my $nw = syswrite( FILE, $buf, $nr ); $tot += $nw; if ( $nr != $nw ) { die "Write failed: $!\n"; } print "$tot / $expected\r"; } close( FILE ); if ( $tot != $expected ) { die "failed: expected $expected, got $tot\n"; } else { print "\n"; } } __DATA__ /pub/linux/redhat/7.1 /pub/rackspace/solaris/archives /pub/linux/redhat/updates/6.2 /pub/rackspace/raq/rpms /pub/linux/redhat/updates/7.0 /pub/rackspace/raq/archives /pub/linux/redhat/updates/7.1 /pub/rackspace/raq/scripts /pub/rackspace/linux/rpms/7.1 /pub/raq/ftp.cobalt.com/pub/artwork /pub/rackspace/linux/archives /pub/raq/ftp.cobalt.com/pub/packages /pub/rackspace/linux/scripts