###
###  Copyright 2000-2004 University of Illinois Board of Trustees
###  All rights reserved. 
###
###  PSGConf::Action::PackageManager::Solaris - Solaris pkg action type for psgconf
###
###  Campus Information Technologies and Educational Services
###  University of Illinois at Urbana-Champaign
###


package PSGConf::Action::PackageManager::Solaris;

use strict;

use PSGConf::Action::PackageManager::FTPArchive;
use PSGConf::Data::String;

use File::Basename;

our @ISA = qw(PSGConf::Action::PackageManager::FTPArchive);


###############################################################################
###  configuration constructor
###############################################################################

sub conf
{
     my ($class, $psgconf) = @_;
	my ($arch, $arch2);

	$arch = `/usr/bin/uname -m`;
	$arch2 = `/usr/bin/uname -p`;
	chomp $arch;
	chomp $arch2;
	if ( $arch eq "i86pc" ) {
		$arch = 'i';

		### Hacks to fix the mess from sunfreeware.com
		$arch2 = '((intel)|(i386)|(x86))';
	} else {
		$arch =~ s/sun4//;
	}

     PSGConf::Action::PackageManager::FTPArchive->conf($psgconf);

     $psgconf->register_data(
		pkg_add_cmd	=> PSGConf::Data::String->new(
						value => "/usr/sbin/pkgadd"
		),
		pkg_rm_cmd	=> PSGConf::Data::String->new(
						value => "/usr/sbin/pkgrm"
		),
		pkg_info_cmd	=> PSGConf::Data::String->new(
						value => "/usr/bin/pkginfo"
		),
		pkg_trans_cmd	=> PSGConf::Data::String->new(
						value => "/usr/bin/pkgtrans"
		),
		pkg_gzip_cmd	=> PSGConf::Data::String->new(
						value => "/usr/bin/gzip"
		),
		pkg_admin_file	=> PSGConf::Data::String->new(
						value => "dontask"
		),
		pkg_dir 		=> PSGConf::Data::String->new(
						value => "/var/sadm/pkg/"
		),
		pkg_regex		=> PSGConf::Data::String->new(
						value => "\-sol[0-9]+\-${arch2}\-local\.gz"
		),
		pkg_arch		=> PSGConf::Data::String->new(
						value => $arch
		)
	);
}

###############################################################################
###  pkg_get_installed() method
###############################################################################

sub pkg_get_installed
{
	my ($self, $psgconf) = @_;
	my (%pkg_installed_list);
	my ($cmd, $pkg, $ver);
	local *FP;

	$cmd = $psgconf->data_obj('pkg_info_cmd')->get();

	$pkg = undef; $ver = undef;
	if (open (FP, "$cmd -l |")) {
		while (<FP>) {
			chomp;

			if ( m/PKGINST: /o ) {
				s/^.*PKGINST: *//;
				$pkg = $_;
			}

			if ( m/VERSION: /o ) {
				s/^.*VERSION: *//;
				s/,REV=/_/;

				###
				### Hacks to clean up the version info from
				### TSM and Emulex Packages
				###
				s/^Release //;
				s/^Version //;
				s/ Release /./;
				s/ Level /./;

				$ver = $_;
			}

			if ( defined $pkg && defined $ver ) {
          		push (@{$pkg_installed_list{$pkg}}, $ver);
				$ver = undef; $pkg = undef;
			}

		}
		close FP;
	} else {
          warn "\t!!! pkginfo failed: $!\n";
	}
	
	return \%pkg_installed_list;
}


###############################################################################
###  pkg_get_latest_version() method
###############################################################################

sub pkg_get_latest_version
{
	my ($self, $psgconf, $pkg) = @_;
	my ($regex) = "(\Q${pkg}\E(\."
		. $psgconf->data_obj('pkg_arch')->get()
		. ")?\-[^-]+)"
		.  $psgconf->data_obj('pkg_regex')->get();

	return $self->get_latest_version (
		$psgconf,
		$pkg,
		$psgconf->data_obj('env_vars')->find('SOL_PKG_UPDATE_PATH'),
		qr{$regex}
		);
}

###############################################################################
###  pkg_get_dependencies() method
###############################################################################

sub pkg_get_dependencies
{
	my ($self, $psgconf, $pkg) = @_;
	my ($pkg2) = $self->Parse($pkg);
	my (@deps, $cmd, $file, $dir, @files);
	my ($regex) = "\Q${pkg}\E" .  $psgconf->data_obj('pkg_regex')->get();
	local *FP;

	### 
	### IF we are working with a datastream package format covert
	### to the directory format
	### 
	if ( ! -d $psgconf->data_obj('pkg_dir')->get() . $pkg2 ) {

		# Else find the package on the server
		# Need to escape the + and . in the package name, hence \Q and \E
		@files = $self->list_files_from_url(
			$psgconf, 
			$psgconf->data_obj('env_vars')->find('SOL_PKG_UPDATE_PATH'),
			qr{$regex});

		# we should have only found one entry
		if ( scalar @files == 1 ) {

			# See if we have the package tarball 'cached'.
			$file = &basename ($files[0]);
			$file = $self->get_file_from_url($psgconf, $files[0])
				if ( ! -f "$psgconf->{tmpdir}/$file" );

			if ( -f $file ) {
     			$cmd = $psgconf->data_obj('pkg_gzip_cmd')->get();
     			$cmd .= ' -d ' .  $psgconf->{tmpdir} . '/' . $file;

          		&PSGConf::Util::RunCommand($cmd);

     			$file =~ s/.gz$//;

				if ( -f $file ) {
     				$cmd = $psgconf->data_obj('pkg_trans_cmd')->get();
     				$cmd .= ' ' . $file . ' . all';
	
					&PSGConf::Util::RunCommand($cmd);
					$dir = $psgconf->{tmpdir} . '/' . $pkg2 . '/install';
				}
			}
		} elsif ( ! scalar @files ) {
			warn "\tCould not find URL for ($pkg)\n"; 
		} else {
			warn "\tFound too many URLs for ($pkg)\n"; 
		}
	} else {
		$dir = $psgconf->data_obj('pkg_dir')->get() . $pkg2 . '/install';
	}

	###
	### Now that we are in directory format (or it is already installed)
	### read the depend file.
	###
	if ( -f "$dir/depend" ) {
		if (open (FP, "$dir/depend"  )) {
			grep (/^P ([^\s]*) /o && (push @deps, $1), <FP>);
			close FP;
		} else {
			warn "\t!!! dependency file ($dir/depend) for ($pkg) could not be read\n";
		}
	}

	return @deps;
}

###############################################################################
###  pkg_install() method
###############################################################################

sub pkg_install
{
	my ($self, $psgconf, $pkg) = @_;
     my ($cmd, $err, $file, @files);
	my ($regex) = "\Q${pkg}\E" .  $psgconf->data_obj('pkg_regex')->get();

	###
	### Find the location of the package
	###
	@files = $self->list_files_from_url(
			$psgconf, 
			$psgconf->data_obj('env_vars')->find('SOL_PKG_UPDATE_PATH'),
			qr{$regex});

	# We must not have found any packages, so return right away
	return -1
		if ( ! scalar @files );

	###
	### Download it it in the tmp area.
	###
	$file = &basename ($files[0]);
	$file = $self->get_file_from_url($psgconf, $files[0])
		if ( ! -f "$psgconf->{tmpdir}/$file" );

	# We must not have downloaded the file, so return right away.
	return -1
		if ( ! -f "$psgconf->{tmpdir}/$file" );

	###
	### Unpack it.
	###
    	$cmd = $psgconf->data_obj('pkg_gzip_cmd')->get();
    	$cmd .= ' -d ' .  $psgconf->{tmpdir} . '/' . $file;

	return $err
		if (($err=&PSGConf::Util::RunCommand($cmd)));

	$file =~ s/.gz$//;

	### 
	### Now that we have the package staged, install it.
	### 
	$cmd = $psgconf->data_obj('pkg_add_cmd')->get();
	$cmd .= ' -na ' . $psgconf->data_obj('pkg_admin_file')->get();
	$cmd .= ' -v '
		if ($psgconf->{verbose});
	$cmd .= ' -d ' . $psgconf->{tmpdir} . '/' . $file;
	$cmd .= ' all';

	$err=&PSGConf::Util::RunCommand($cmd);

     print "\n"
          if ($psgconf->{verbose});

	return $err;
}

###############################################################################
###  pkg_remove() method
###############################################################################

sub pkg_remove
{
	my ($self, $psgconf, $pkg, $force) = @_;
	my ($cmd, $err);


	###
	### Remove the version of the package if we have 
	### pkg_number_of_versions set to 1
	###
	($pkg) = $self->Parse($pkg)
		if ( $self->{number_of_versions} <= 1 );

	###
	### The force option is actually solved via the admin file.
	###

	$cmd = $psgconf->data_obj('pkg_rm_cmd')->get();
	$cmd .= ' -na ' . $psgconf->data_obj('pkg_admin_file')->get();
	$cmd .= ' -v'
          if ($psgconf->{verbose});
	$cmd .= ' ' . $pkg;

	$err=&PSGConf::Util::RunCommand($cmd);

     print "\n"
          if ($psgconf->{verbose});

     return $err;
}


###############################################################################
###  documentation
###############################################################################

1;

__END__

=head1 NAME

PSGConf::Action::PackageManager::Solaris - install Solaris packages

=head1 SYNOPSIS

  use PSGConf::Action::PackageManager::Solaris;

  $psgconf->register_actions(
		PSGConf::Action::PackageManager::Solaris->new(
			'quiet'		=> 1
		),
		...
	);

=head1 DESCRIPTION

The B<PSGConf::Action::PackageManager::Solaris> module provides a B<PSGConf>
action class for updating Solaris packages.

The B<PSGConf::Action::PackageManager::Solaris> class is derived from the
B<PSGConf::Action::PackageManager::FTPArchive> class, but it defines/overrides
the following methods:

=over 4

=item I<pkg_get_installed()>

A method that will assign values to the I<pkg_installed_list>, which
will be all the packages currently installed.  The I<pkg_installed_list>
is a reference to a hash of arrays.  The array is sorted to have
versions in ascending order.  Called from the 
B<PSGConf::Action::PackageManager::check()> method.

=item I<pkg_get_latest_version($pkg)>

A method that will return a B<PSGConf::Data::String> that contains
the latest version of $pkg from the package repository.  Called from the 
B<PSGConf::Action::PackageManager::check()> method.

=item I<pkg_get_dependencies($pkg)>

A method that will return a B<PSGConf::Data::List> that contains
all the packages that $pkg will depend upon, regardless
if they have been installed or not.   This is for the package on
the package repository and $pkg will have the version in it as well.
Called from the B<PSGConf::Action::PackageManager::diff()> method.

=item I<pkg_install($pkg)>

A method that will install a $pkg on the system.  Returns 0 for a
successfull install, non-zero for a failure.  Called from 
B<PSGConf::Action::PackageManager::do()> method.

=item I<pkg_remove($pkg, $force)>

A method that will remove $pkg from the system, and may have to force
the removal, depending on the value of $force, which will be a
B<PSGConf::Data::Boolean>.  Returns 0 for a successfull install,
non-zero for a failure.  Called from 
B<PSGConf::Action::PackageManager::do()> method.


=back


The constructor also registers the following data objects:

=over 4

=item I<pkg_add_cmd>

A B<PSGConf::Data::String> object that determines where the F<pkgadd>
command is.  The default is I</usr/sbin/pkgadd>.

=item I<pkg_rm_cmd>

A B<PSGConf::Data::String> object that determines where the F<pkgrm>
command is.  The default is I</usr/sbin/pkgrm>.

=item I<pkg_info_cmd>

A B<PSGConf::Data::String> object that determines where the F<pkginfo>
command is.  The default is I</usr/bin/pkginfo>.

=item I<pkg_trans_cmd>

A B<PSGConf::Data::String> object that determines where the F<pkgtrans>
command is.  The default is I</usr/bin/pkgtrans>.

=item I<pkg_gzip_cmd>

A B<PSGConf::Data::String> object that determines where gzip is located.
Used to unpack the data stream for installation.
The default is I</usr/bin/gzip>.

=item I<pkg_admin_file>

A B<PSGConf::Data::String> object that determines where the F<pkgadd>
and F<pkgrm> command will use for the admin file, C<-a> option.
The default is I<dontask>.

=item I<pkg_dir>

A B<PSGConf::Data::String> object that determines where the information
related to installed packages is stored.  The default is I</var/sadm/pkg/>.

=item I<pkg_regex>

A B<PSGConf::Data::String> object that determines what the regex is to
match against the FTPArchive listing.
The default is I<\-sol[0-9]+\-(sparc|intel)\-local\.gz>.

=item I<pkg_arch>

A B<PSGConf::Data::String> object that determines the specific package
architecture for installing SVR4 packages.  Defaults to the output
of C<uname -m>, sans F<sun4>, on sparc platforms and I<i> on x86.

=back


=head1 SEE ALSO

L<perl>

L<PSGConf>

L<PSGConf::Action>

L<PSGConf::Action::PackageManager::FTPArchive>

L<PSGConf::Data::String>

L<File::Basename>

C<CE<lt>pkgadd(1)E<gt>>

C<CE<lt>pkginfo(1)E<gt>>

C<CE<lt>pkgrm(1)E<gt>>

=cut



syntax highlighted by Code2HTML, v. 0.9.1