#!/usr/bin/perl -w # # An alternate SIS installer. installer.pl is the original. # # $Id: install 13 2007-10-04 21:00:11Z arighi $ # # # Based on installer.pl, which was created by Sean Dague, and contributed to # by Michael Chase-Salerno, Dann Frazier, and Brian Finley. # # 2003.12.08 Brian Elliott Finley # - Specify specific packages to install, rather than classes of install. # The variation of install combinations we now have caused me to try # this new method of install for this script. # 2004.01.31 Brian Elliott Finley # - Added dependency checking # - Auto-add dependencies if in package list # - Check integrity of each existing rpm before deciding not to download # 2004.06.29 Brian Elliott Finley # - if --list and --download-only, just --list # 2005.12.21 Paul Greidanus # - allow user to choose a specific sf mirror # 2005.12.22 Paul Greidanus # - added --mirror-list, and move --sfsite to --mirror # 2005.12.22 Brian Elliott Finley # - verbiage tweak # # Run "./install --help" for description. # ################################################################################ # # TODO # - upon rpm install failure, spit out bit about TK XML-simple and other rpms # - use local list file if it exists. # use Cwd; use Carp; use Data::Dumper; use strict; use File::Copy; use File::Path; use File::Spec; use Getopt::Long; use POSIX qw(uname); # Tags allow for an arbitrary slice of software. I'm fully qualifying # the url here so that we can have tags point to somewhere else # for people to maintain. # # New tag policy: anyone who wants a tag, and asks nicely, gets one. my %packagelist_urls = ( # stable is what we consider latest stable code stable => 'http://download.systemimager.org/pub/sis-install/tags/stable.list', # devel is for development-release available packages devel => 'http://download.systemimager.org/pub/sis-install/tags/devel.list', # CSM is for the Cluster Systems Management group at IBM # ping salernom@us.ibm.com or sldague@us.ibm.com for any # other info about it. "CSM" => 'http://download.systemimager.org/pub/sis-install/tags/CSM.list', # add other tag urls here... please put in a description of who uses it, # and how to contact them. ); our %opt; $opt{directory} = "/tmp/sis-packages"; GetOptions ( "help" => \my $help, "verbose|v" => \$opt{verbose}, "vverbose|vv" => \$opt{vverbose}, "list" => \my $list, "tag=s" => \my $tag, "package-list=s" => \$opt{package_list}, "download-only" => \$opt{download_only}, "directory=s" => \$opt{directory}, "mirror=s" => \$opt{mirror}, "mirror-list" => \my $mirrorlist, ) or usage(1); ################################################################################ # # option validation # # Show help if($help) { usage(0); } if($opt{vverbose}){ $opt{verbose} = 1; } # tag to use if($tag) { if(!$packagelist_urls{$tag}) { print qq(Unknown tag: "$tag"\n); usage(1); } } else { # default $tag = "stable"; } unless(($list) or ($opt{download_only}) or ($ARGV[0]) or $mirrorlist) { usage(1); } if ($mirrorlist) { &list_mirrors; exit(0); } # create download dir if it doesn't exist if(!-d $opt{directory}) { mkdir $opt{directory}, 0775; } # copy installer to download directory unless( (-e "$opt{directory}/install") && ((stat("$0"))[1] == (stat("$opt{directory}/install"))[1]) ) { copy($0, "$opt{directory}/install") or croak("Couldn't copy $0 to $opt{directory}"); chmod 0755, "$opt{directory}/install"; } # if specified if($opt{package_list}) { # Deal with relative paths like "./" and such $opt{package_list} = File::Spec->rel2abs($opt{package_list}, cwd()); if(! -e "$opt{package_list}") { carp("File name $opt{package_list} doesn't exist.\n"); usage(1); } } else { # use default $opt{package_list} = "$opt{directory}/${tag}.list"; # use local if it exists, download if necessary if(! -e "$opt{package_list}") { download_file($packagelist_urls{$tag}); } else { print "Using pre-existing package list: $opt{package_list}\n"; } } if($list) { &list_packages; exit 0; } # Attempt to auto-detect the type of packages supported by the distribution. chomp(my $package_type = `type -p dpkg || type -p rpm`); if ($package_type =~ /dpkg/) { print "DEB-based distribution detected\n" if ($opt{verbose}); $package_type = 'deb'; } elsif ($package_type =~ /rpm/) { print "RPM-based distribution detected\n" if ($opt{verbose}); $package_type = 'rpm'; } else { print "ERROR: Unable to detect supported package type (trying with RPM)\n"; $package_type = 'rpm'; } if($opt{download_only} && !$ARGV[0]) { print "ERROR: Must specify one or more packages with --download-only.\n"; print " Try --list to see available packages.\n\n"; usage(1); } if($opt{download_only}) { &download_packages; } else { &install_packages; } parting_message() if($opt{verbose}); exit(0); ################################################################################ # # sub routines follow # # # Usage: if( rpm_has_integrity($rpm) ) { ...; } # sub rpm_has_integrity { my $rpm = shift; if($opt{verbose}) { print "Checking integrity of "; } my $cmd = "rpm --checksig $rpm"; unless($opt{verbose}) { $cmd .= " 1>/dev/null 2>/dev/null"; } !system($cmd) or return undef; # rpm failed test return 1; # rpm passed } # # usage: download_file($url) # sub download_file { my $url = shift; my $wget_opts = "--timestamping --directory-prefix=$opt{directory}"; unless($opt{vverbose}) { $wget_opts .= " --quiet"; } if ($opt{mirror} && $url =~ m/install.sisuite.org/) { $url =~ s^http://install.sisuite.org/sourceforge^^; $url = "http://$opt{mirror}.dl.sourceforge.net/sourceforge" . $url; } my $cmd = "wget $wget_opts $url"; print "Downloading: $url..." if($opt{verbose}); print "\n$cmd\n" if($opt{vverbose}); !system($cmd) or croak("$cmd failed!"); if($opt{vverbose}) { print "\n"; } elsif($opt{verbose}) { print "done!\n"; } return 1; } # # basic routine that downloads packages based on entries in the # package.lst file # sub download_packages { my %package_urls = get_package_urls(); foreach my $package ( @ARGV ) { if(!$package_urls{$package}) { print qq(\nERROR: package "$package" does not exist in $opt{package_list}\n\n); usage(1); } my $url = $package_urls{$package}; download_file($url); } } sub querytag { my ($tag, $file, $installed) = @_; my $output = ""; if($installed) { # work against the installed package, not the file $output = `rpm -q --queryformat '\%{$tag}' $file 2>/dev/null`; } else { $output = `rpm -qp --queryformat '\%{$tag}' $file 2>/dev/null`; } return $output; } sub install_packages { my @install = (); my $rpm; my %rpm_names = get_rpm_names(); my %package_urls = get_package_urls(); chdir $opt{directory}; # download if we don't already have the package foreach my $package (@ARGV) { if(!$package_urls{$package}) { print qq(\nERROR: package "$package" does not exist in $opt{package_list}\n\n); usage(1); } my $rpm = "$rpm_names{$package}"; unless( (-e "$rpm") && (rpm_has_integrity($rpm)) ) { my $url = $package_urls{$package}; download_file($url); } # # check for dependencies # my $dependency_pkg; my $cmd = "rpm -q --requires -p $rpm"; open(INPUT,"$cmd|") or croak("Couldn't $cmd"); while() { ($dependency_pkg) = split; if( ($package_urls{$dependency_pkg}) and ( ! grep { /^$dependency_pkg$/ } @ARGV) ){ push(@ARGV, $dependency_pkg) } } close(INPUT); } # verify that packages aren't already installed foreach my $package (@ARGV) { my $rpm = "$rpm_names{$package}"; if(not_installed($package,"$rpm")) { push @install, $rpm; } } if(scalar(@install)) { my $cmd = "rpm -U"; $cmd .= "hv" if($opt{verbose}); $cmd .= " " . (join ' ', @install); print "$cmd\n" if($opt{verbose}); # # If the system command succeeds, it will return zero, hence the ! for perl. # unless(!system($cmd)) { exit 1; } print "The System Installation Suite packages you've chosen are now installed!\n"; } else { print "The System Installation Suite packages you've chosen are already installed.\n"; } } # # Query the host rpm db. If the release and version aren't exactly # the same as the file we're going to try to install, we've got a newer # package (or all hell broke loose and they are trying to use this # tool to downgrade... NOT SUPPORTED FUNCTIONALITY) # # Usage: if( not_installed("rpm_name","rpm_file_name.rpm") ) { do stuff; } # sub not_installed { my $package = shift; my $rpm = shift; my $instver = querytag('VERSION',$package,1); my $instrel = querytag('RELEASE',$package,1); my $filever = querytag('VERSION',$rpm,0); my $filerel = querytag('RELEASE',$rpm,0); unless(($instver cmp $filever) == 0 and ($instrel cmp $filerel) == 0) { return 1; } return 0; } # # returns a hash of packages -> rpm names # # Usage: my %rpm_names = get_rpm_names(); # sub get_rpm_names { my %rpm_names; # get available packages open(FILE,"<$opt{package_list}") or croak("Can't open $opt{package_list}"); while() { chomp; next if(m/^\s*#/); next if(m/^$/); my $rpm_name = $_; $rpm_name =~ s{^.*/}{}; # remove url part my $package = remove_version($rpm_name); # remove version $rpm_names{$package} = $rpm_name; # store naked pkg name } close(FILE); return %rpm_names; } # # returns a hash of packages -> their_urls # # Usage: my %package_urls = get_package_urls(); # sub get_package_urls { my %package_urls; # get available packages open(FILE,"<$opt{package_list}") or croak("Can't open $opt{package_list}"); while() { chomp; next if(m/^\s*#/); next if(m/^$/); next unless(m/$package_type$/); my $url = $_; my $package = $_; $package =~ s{^.*/}{}; # remove url part $package = remove_version($package); # remove version $package_urls{$package} = $url; # store naked pkg name } close(FILE); return %package_urls; } sub list_packages { my %package_urls = get_package_urls(); print qq(Packages available for download and/or install:\n\n); # show available packages in alphabetical order foreach (sort(keys %package_urls)) { if($opt{vverbose}) { print " $package_urls{$_}\n"; } else { print " $_\n"; } } print qq(\n); print qq(To install packages, do a:\n\n); print qq( ./install --verbose PKG1 PKG2 ...\n\n); print qq(For example:\n\n); print qq( ./install --verbose systemconfigurator perl-AppConfig\n); print qq(\n); return 1; } # This is a horrible hack to get the sourceforge mirrors list, but I can't seem to find another way # Paul Greidanus (paul.greidanus@ualberta.ca) sub list_mirrors { my $line; my $cmd = "wget -q -O - http://prdownloads.sourceforge.net/systeminstaller/systeminstaller-1.04-1.noarch.rpm 2> /dev/null | grep form.submit | grep value | grep -v none"; my $mirror_sites = `$cmd`; $mirror_sites =~ s#<[^>]*>##g; print "The following is a list of mirrors that are currently available for use with the\n"; print "--mirror command. They are listed in the form of \"MIRROR (LL)\". LL indicates\n"; print "the country where the mirror is located, and \"MIRROR\" is the argument to use\n"; print "with the --mirror option.\n\n"; print "$mirror_sites\n"; } sub usage { my $exit_status = shift; unless(defined($exit_status)) { $exit_status = 1; }; my $tags = join "|", (sort keys %packagelist_urls); my $tags2 = join '", "', (sort keys %packagelist_urls); $tags2 = '"' . $tags2 . '"'; print <, or join us in IRC on irc.freenode.net, channel "#sisuite". If you are using a development release, please send constructive feedback to . You can find documentation on SystemInstaller, SystemImager, and System Configurator (Three Easy Pieces) in /usr/share/doc, or at one of these friendly neighborhood websites: http://systeminstaller.sourceforge.net/ http://www.systemimager.org/ http://systemconfig.sourceforge.net/ ################################################################################ END } # # Usage: $name = remove_version($name); # sub remove_version { $_ = shift; # extract package name, without version, from package name m#([\w-]+)[-_].*$#; return $1; }