#!/usr/bin/perl # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "@(#)pkg-install 1.2 07/09/17 SMI" # # # install packages (and their dependencies) # This includes support for network repositories as well. Packages on # the network must be a package stream at their core. They can be # compressed via compress(1), gzip(1), or bzip2(1). # strict; use Getopt::Long; use File::Basename qw/ basename /; use File::Temp qw/ tempdir tmpnam /; use Cwd qw/ getcwd /; open(P, "uname -p |") && ($arch =

) && chop($arch) && close(P); my (@repositories, $root, $verbose, $dryrun) = (); my $tmpdir = tempdir(CLEANUP=>1); my $pkgspool = '/var/tmp/pkgs'; my $pkgadmin = "$tmpdir/admin.$$"; my $pkgadd = "/usr/sbin/pkgadd -n -a $pkgadmin -S"; my $wget = "/usr/sfw/bin/wget -q"; my @suffixes = ('', '.pkg', '.bz2', '.bz', '.gz', '.pkg', '.pkg.bz2', ',pkg.bz', '.pkg.gz', "-$arch", "-$arch.bz2", "-$arch.bz", "-$arch.gz"); my %pkgs = (); $| = 1; sub execute { local ($cmd) = @_; defined($verbose) && print(STDERR "$cmd\n"); return (system($cmd)); } sub create_admin_file { local ($file) = @_; open(FH, ">$file"); print FH <{PKG}\n"); print("name:\t\t$pkg->{NAME}\n"); print("version:\t$pkg->{VERSION}\n"); print("category:\t\t$pkg->{CATEGORY}\n"); print("source:\t\t$pkg->{SOURCE}\n"); print("required:\t$pkg->{REQUIRED}\n"); print("conflicting:\t$pkg->{CONFLICTING}\n"); } sub read_pkginfo_file { local ($path, $pkg) = @_; open(FH, "<$path"); while () { if (/^PKG=(.*)/) { $pkg->{PKG} = $1; } elsif (/^NAME=(.*)/) { $pkg->{NAME} = $1; } elsif (/^VERSION=(.*)/) { $pkg->{VERSION} = $1; } elsif (/^CATEGORY=(.*)/) { $pkg->{CATEGORY} = $1; } } close(FH); return ($pkg); } sub read_depend_file { local ($path, $pkg) = @_; local (@conflicting, @required) = (); open(FH, "<$path"); while () { if (/^I\s+([^\s]+)/) { push(@conflicting, $1); } elsif (/^P\s+([^\s]+)/) { push(@required, $1); } } close(FH); defined(@conflicting) && ($pkg->{CONFLICTING} = "@conflicting"); defined(@required) && ($pkg->{REQUIRED} = "@required"); return ($pkg); } sub read_package_data { local ($path) = @_; my $pkg; $pkg = read_pkginfo_file("$path/pkginfo", $pkg); $pkg = read_depend_file("$path/install/depend", $pkg); return $pkg; } sub base_stream_command { local ($stream) = @_; if ($stream =~ /\.Z$/) { return("zcat $stream"); } elsif ($stream =~ /\.gz$/) { return("gzip -d -c $stream"); } elsif ($stream =~ /\.bz[2]*$/) { return("bzip2 -d -c $stream"); } else { return("cat $stream"); } } sub package_data_from_source { local ($source, $pkgname) = @_; my $dir = $tmpdir; if (-d "$source/$pkgname") { $dir = $source; } elsif ((-s $source) && (! -d "$tmpdir/$pkgname")) { my $cwd = Cwd::getcwd(); my $blocks = 0, $cmd = base_stream_command($source); # extract the pkginfo/pkgmap files if (chdir($tmpdir)) { my $scmd = $cmd." | dd skip=1"; $scmd = $scmd." | cpio -dumi 2>&1"; open(FH, $scmd." |"); $blocks = ; close(FH); ($blocks =~ /(\d+) blocks/) && ($blocks += 1); } # extract the pkg depend file if (($blocks != 0) && chdir($pkgname)) { my $scmd = $cmd." | dd skip=".$blocks." 2>/dev/null"; $scmd = $scmd." | cpio -dumi install/depend 2>/dev/null"; execute($scmd); } if (! -f "install/depend") { $blocks += 1; my $scmd = $cmd." | dd skip=".$blocks." 2>/dev/null"; $scmd = $scmd." | cpio -dumi install/depend 2>/dev/null"; execute($scmd); } chdir($cwd); } return (read_package_data("$dir/$pkgname")); } sub install_package { local ($src, $pkgname, $ostream) = @_; my $result = 0; defined($ostream) && print("$pkgname from $ostream($src)\n") || print("$pkgname from $src\n"); (!defined($dryrun)) && ($result = execute($pkgadd." -d $src $pkgname")); return ($result); } sub install_package_from_stream { local ($stream, $pkgname) = @_; my $tmp = tmpnam(), $ostream = $stream; my $cmd = base_stream_command($stream)." >$tmp"; if ($cmd !~ /^cat /) { system($cmd); $stream = $tmp; } my $result = install_package($stream, $pkgname, $ostream); unlink($tmp); return ($result); } sub install_package_from_source { local ($source, $pkgname) = @_; my $result = 0; if (-d $source) { $result = install_package($source, $pkgname); } else { $result = install_package_from_stream($source, $pkgname); } return ($result); } sub download_package_from_repository { local ($repository, $pkgname) = @_; if (! -d $repository) { foreach $suffix (@suffixes) { my $dest = "$pkgspool/$pkgname$suffix"; my $cmd = $wget." -O $dest $repository/$pkgname$suffix"; if ((execute($cmd) == 0) && (-s $dest)) { return ($dest); } unlink($dest); } } return; } sub retrieve_package_from_network { local ($pkgname) = @_; my $repository; foreach $repository (@repositories) { my $path; if (-d $repository) { (-d "$repository/$pkgname") && return($repository); foreach $suffix (@suffixes) { my $name = "$repository/$pkgname$suffix"; if (-s $name) { $path = $name; } } } else { $path = download_package_from_repository($repository, $pkgname); } defined($path) && return ($path); } return; } sub download_packages { local (@packages) = @_; my $pkgname; foreach $pkgname (@packages) { my $pkg, $path; (defined($pkgs{$pkgname})) && next; print("$pkgname..."); if (pkg_is_installed($pkgname) == 1) { print("already installed\n"); $pkgs{$pkgname} = 'installed'; next; } $path = retrieve_package_from_network($pkgname); if (!defined($path)) { print("not found\n"); exit(1); } print($path."\n"); $pkg = package_data_from_source($path, $pkgname); $pkg->{SOURCE} = $path; $pkgs{$pkgname} = $pkg; if (defined($pkg->{REQUIRED})) { print(" (requires: $pkg->{REQUIRED})\n"); download_packages(split(/ /, $pkg->{REQUIRED})); } } } sub add_package { local ($pkgname) = @_; my $pkg = $pkgs{$pkgname}; ($pkg eq 'installed') && return; if (!pkg_is_installed($pkgname)) { add_packages(split(/ /, $pkg->{REQUIRED})); ($pkg->{CATEGORY} ne 'pseudo') && install_package_from_source($pkg->{SOURCE}, $pkgname); } } sub add_packages { local (@packages) = @_; local ($pkgname) = (); foreach $pkgname (@packages) { add_package($pkgname); } } # Main execution begins here... GetOptions('R|root=s' => \$root, 'repository=s' => \@cli_repositories, 'pkgspool=s' => \$pkgspool, 'verbose' => \$verbose, 'dryrun' => \$dryrun); defined($root) && ($pkgadd = $pkgadd." -R $root"); push(@repositories, "$pkgspool"); push(@repositories, "$root/var/spool/pkg"); @repositories = split(/ /, "@repositories @cli_repositories"); # This is where the companion bits are stored push(@repositories, "http://dlc.sun.com/osol/companion/downloads/current/pkgs/$arch"); print("Download/Install packages:\n"); foreach $tmp (@ARGV) { print("\t$tmp\n"); } print("from repositories:\n"); foreach $tmp (@repositories) { print("\t$tmp\n"); } defined($root) && (print("to: $root\n")); create_admin_file($pkgadmin); print("Locating and downloading packages for installation\n"); download_packages(@ARGV); print("Installing packages\n"); add_packages(@ARGV); print("copies of downloaded packages were left in $pkgspool\n"); exit 0;