#!/usr/bin/perl # dlkern v27, created by Darxus@ChaosReigns.com. # # Queries ftp.kernel.org for current Linux kernel versions, via finger, # and then downloads the requested versions (stable/beta/pre-patch) as specified # by commandline flags, from ftp.kernel.org using ncftpget or wget. # # Execute "dlkern -h" for usage. # # Example: # # dlkern -s -c us # # will download the current stable (-s) version from the United States (-c us) # mirror. It is strongly recommended that you use -c to specify your 2 letter # country code, from the list at http://www.kernel.org/mirrors/ but it is not # manditory. # # This program is is released under the GNU GPL (http://www.gnu.org/copyleft/gpl.html) # # This program requires the Net::Finger Perl module which can be obtained from: # Debian package: libnet-finger-perl # RedHat package: perl-Net-Finger # Others: http://search.cpan.org/search?dist=Net-Finger\n\n"; # # TODO: # * Make more cron friendly ? # * Use uname & if_exist output_file to see if it actually needs to be downloaded. # * Debian package. # * RedHat package. # * use strict. # * Check signitures w/ GnuPG.pm (http://indev.insu.com/GnuPG/) # # v15 Jan 9 20:28 EST 1st release. # v16 Jan 20 01:09 EST Changed finger address from "@linux.kernel.org" (not # working) to "@ftp.kernel.org". # v17 Jan 21 14:43 EST Code cleanup & documentation. # v18 Jan 21 15:19 EST wget support. # v19 Jan 21 15:29 EST Handle prepatch version "none". # v20 Jan 21 15:59 EST Handle failed finger. # v21 Jan 21 16:18 EST Output directory argument. # v22 Jan 21 16:47 EST Added support for The Linux Kernel Archive Mirror System. # v23 Jan 21 16:54 EST Allow specification of filetype (.gz/.bz2). # v23 Jan 21 17:00 EST -h (usage) flag. # v24 Jan 21 17:18 EST Fixed a couple minor warnings. # v25 Jan 21 18:34 EST Automatically download signitures (inebrieated). # v26 Jun 13 16:32 EDT Removed beginnings of signiture verification so # signiture download could work cleanly. # v27 Jun 14 18:02 EDT Automatic signiture verification. # # I decided I don't like standard version numbers. The version numbers for this # program started at "1", and have been incremented every time I made a change. # Numbers before 15 were not fit for public consumption. # Load the Net::Finger module so I don't have to do any work to get the version # info via finger. use Net::Finger; # Load getopts to read commandline options. require 'getopts.pl'; if (!eval "require 'GnuPG.pm';") { print "No GnuPG, cannot verify signitures.\n"; $gnupg=0; } else { print "GnuPG.pm loaded.\n"; $gnupg=1; $gpg = new GnuPG(); %trust = (-1, "UNDEFINED", 0, "NEVER", 1, "MARGINAL", 2, "FULLY", 3, "ULTIMATE"); } # Read commandline options. &Getopts('sbpwnh:d:c:e:'); sub usage { print " Usage: dlkern [-sbp] [-nw] [-d ] [-c ] [-e ] -s Download stable version of Linux kernel. -b Download beta version of Linux kernel. -p Download prepatch version of Linux kernel. -n Use ncftpget as downloader (default). -w Use wget as downloader. -d Specify output directory. -c Specify 2 letter country code from http://www.kernel.org/mirrors/ -e Specify extension (bz2 is more compressed). -h Show this usage information. " unless ($usage); $usage=1; } # If there are no commandline options, display usage. Normally you'd exit after # displaying usage, but I keep going to display the URLs. unless ($opt_s or $opt_b or $opt_p) { &usage; } # Check kernel version flags, store info in the #dl hash, with the version # type as the key. if ($opt_s) { $dl{"stable"}=1; } if ($opt_b) { $dl{"beta"}=1; } if ($opt_p) { $dl{"prepatch"}=1; } if ($opt_d) { $dir = $opt_d; } if ($opt_c) { $country = ".$opt_c"; } if ($opt_e) { $ext = $opt_e; } else { $ext = "gz"; } if ($opt_h) { &usage; exit; } # Create a list of version types (is "trees" an appropriate name?) @trees = ("stable", "beta", "prepatch"); # Use info from the commandline to list the versions that were requested. print "Requested:"; foreach $tree (@trees) { if ($dl{$tree}) { print " $tree"; } } print ".\n"; # Check downloader flags. if (($opt_w && $opt_n)) { &usage; } elsif ($opt_w) { $downloader="wget"; } else { $downloader="ncftpget"; } if ($downloader) { print "Downloader is $downloader.\n"; } print "\n"; print "Retrieving version numbers.\n"; # Query finger data. @lines = finger('@ftp.kernel.org', 1) or die "Failed to obtain version info by fingering \@ftp.kernel.org.\n"; # Parse finger data into URLs. Store them in the hash #url, using the tree # name for the key. foreach $line (@lines) { #Split each line into the description & version, using a ":" as a delimiter. ($desc, $ver) = split /:/, $line; #Strip spaces out of $ver. $ver =~ tr/" \n"//d; foreach $tree (@trees) { if ($desc =~ $tree) { $version{$tree} = $ver; print "$tree: $version{$tree} "; if ($tree eq "stable" or $tree eq "beta") { # For kernel version 2.3.40, set $first equal to "2", and $second equal # to "3", so this thing still works when 2.4.x or 3.x.x happens, as long # as their path naming conventions stay the same. ($first, $second, $rest) = split /\./, $version{$tree}; $url{$tree} = "ftp://ftp$country.kernel.org/pub/linux/kernel/v$first.$second/linux-$version{$tree}.tar.$ext"; } elsif ($ver eq "none") { $url{$tree} = ""; } else { $url{$tree} = "ftp://ftp$country.kernel.org/pub/linux/kernel/testing/pre-patch-$version{$tree}.$ext"; } print "$url{$tree}\n"; } } } print "\n"; if ($dir) { print "Changing to output directory: $dir\n"; chdir ($dir) or die "Can't cd to $dir $!\n"; } # Call ncftpget for each requested tree. foreach $tree (@trees) { if ($dl{$tree}) { if ($url{$tree}) { print "Downloading $tree by calling \"$downloader $url{$tree}\"\n"; system ($downloader, "$url{$tree}"); print "Downloading $tree signiture by calling \"$downloader $url{$tree}.sign\"\n"; system ($downloader, "$url{$tree}.sign"); print "Verifying signiture.\n"; #linux-2.2.14.tar.gz.sign #$sig = $gpg->verify( signature => "linux-$version{$tree}.tar.$ext.sign", file => "linux-$version{$tree}.tar.$ext" ); if (eval { $sig = $gpg->verify( signature => "linux-$version{$tree}.tar.$ext.sign", file => "linux-$version{$tree}.tar.$ext" );}) { print "Signiture passed verification.\n"; print "Signiture info:\n"; for $key (sort(keys(%$sig))) { print "$key: $$sig{$key}\n"; } print "\nTimestamp: ",scalar(localtime($$sig{"timestamp"})),"\n"; print "Trust level for this signiture is: $trust{$$sig{trust}}\n"; } else { print "SIGNITURE DID NOT PASS VERIFICATION\n"; } } else { print "There is currently no $tree version.\n"; } } }