#!/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;