#!/usr/bin/perl 

########## Confixx(R) 3.0 Professional ############
####### Copyright SWsoft, Inc. 2004-2005 ##########
#### http://www.sw-soft.com - info@sw-soft.com ####

use File::Copy;
use File::Basename;
use Getopt::Long;
use Pod::Usage;

BEGIN {
  if ($0=~/chroot_install.pl/){
    $chrootDoMode=0;
  }else{
    $chrootDoMode=1;
  }
}

#
# Confixx library
#
use lib_module_common;

&initConfig;

unless($chrootDoMode){
  $ret = &chrootGo;

  if ($0=~/chroot_install.pl/){
	exit $ret;
  }else{
	return $ret;
  }
}else {
  $chrootDoMode=1;
}

sub chrootGo {
  my $ptrChrootedBin=shift;
  my $ptrBin;
  my $chrootDir;
  my $modeDo;

  unless (ref($ptrChrootedBin)=~/ARRAY/){

    Getopt::Long::Configure('prefix_pattern=--|-','ignore_case');

    @config_opts = (
		  'r|root=s',
		  'ldd=s',
		  'f|force',
		  'h',
		  'help',
		  'v|version'
		 );

    unless ( &GetOptions(\%args, @config_opts)){
      pod2usage({ -msg => "$0: Errors in the command line found",
		-exitval => 1} );
    }

    if ( $args{'h'} ){
      pod2usage( {-exitval => 0} );
    }

    if ( $args{'help'} ){
      pod2usage( {-verbose => 2, 
		-exitval => 0} );
    }
    
    unless( @ARGV){
      pod2usage( {-msg => "$0: Error: no program given\n",
	  -exitval => 2 } );
    }
    $ptrChrootedBin=\@ARGV;

  }else{
    $modeDo=1;
    $chrootDir=shift;
    $args{'r'}=$chrootDir;
    $ptrBin=shift || \%bin;
    $args{'ldd'}=$ptrBin->{'ldd'};
    $args{'f'}=shift;
  }

  my $wDir = dirname($0);
  my $subs_include_files = "$wDir/subs_include_files.pl";
  if (-x "$wDir/subs/subs_include_files.pl"){
    $subs_include_files = "$wDir/subs/subs_include_files.pl";
  }
  my $subs_include_questions = "$wDir/subs_include_questions.pl";
  if (-x "$wDir/subs/subs_include_questions.pl"){
    $subs_include_questions = "$wDir/subs/subs_include_questions.pl";
  }

  my @need_binaries_chroot = ('ldd','cp','ldconfig');

  do $subs_include_files;
  do $subs_include_questions;

  foreach my $binary (@need_binaries_chroot){
    unless(-x $ptrBin->{$binary}){
      $ptrBin->{$binary} = &getBin($binary);
    }
  }

  if (-x $ptrBin->{'cp'}){
    $bin_cp=$ptrBin->{'cp'};
  }
  unless ( -x $bin_cp){
    $bin_cp='';
  }

  if (-x $ptrBin->{'ldconfig'}){
    $bin_ldconfig=$ptrBin->{'ldconfig'};
  }
  unless ( -x $bin_ldconfig){
    $bin_ldconfig='';
  }


  if (-x $args{'ldd'}){
    $ptrBin->{'ldd'}=$args{'ldd'};
  }
  unless ( -x $ptrBin->{'ldd'} ){
    $bin_ldd = &BinQuestion (&ltext('install_bin_path','ldd'),'ldd');
    $ptrBin->{'ldd'} = $bin_ldd;
  }else{
    $bin_ldd = $ptrBin->{'ldd'};
  }



  unless ($chrootDir=$args{'r'}){
    if ($scponly_chroot){
      $chrootDir="$user_homeDir/$scponly_chroot";
      $chrootDir=''  unless (-d $chrootDir);
    }
  }
  
  my $chrootName = $scponly_chroot || 'chroot';
  while ($chrootDir =~ /^\/?$/){
    
    $chrootDir = &PathQuestion( &ltext('install_question_chrootdir'), 
				"$user_homeDir/".($scponly_chroot||'chroot'));
    while ($chrootDir =~ /(.*)\/+$/ ){
      $chrootDir=$1;
    }
  }

  $force_rewrite = $args{'f'}||0;

  unless (-d $chrootDir) {
    &RecCreateDir($chrootDir,0755);
  }else{
    if ($force_rewrite){
      chmod ($chrootDir,0755);
    }
  }

  unless (-d $chrootDir){
    if ($modeDo){
      warn "Can't create directory $chrootDir\n";
      return 0;
    }else{
      die "Can't create directory $chrootDir\n";
    }
  }

  my ($fullPath,$dir,$name,$ext,$fullName,%so,%prog,@names,@libs,$rewrite,
      $i,$dstPath,$srcPath,$mode,$srcDir, $dstName, $scrLink,@links,@forAdd,$link);

  foreach my $prog (@{$ptrChrootedBin}){
    print "ldd query program: $prog\n";
    unless (-x $prog) {
      print "$prog has not execution permission. Skipped\n";
      next;
    }
    open (LDD,"$bin_ldd $prog |") or die "Can't run program: $!\n";
    while(<LDD>){
      if (/=>\s*(\/\S+)/){
	$fullPath=$1;
	($dir,$name,$ext)=&parsePath($fullPath);
	if ($ext eq 'so'){
	  push (@{$so{$dir}},$name);
	}
      }
    }
    close (LDD);
    ($dir,$name,$ext,$fullName)=&parsePath($prog);
    push (@{$prog{$dir}},$fullName);
  }
  my (%ldDirs,$npp);
  if (-x $bin_ldconfig){
    if(open(LDCONF,"< $chrootDir/etc/ld.so.conf")){
      $npp=0;
      while (<LDCONF>){
	chomp;
	$dir=$_;
	if (-d "$chrootDir$dir"){
	  unless(exists($ldDirs{$dir})){
	    $npp++;
	    $ldDirs{$dir}=$npp;
	  }
	}
      }
      close(LDCONF);
    }
  }

  foreach $srcDir (keys %so){

    print "Create dir: $chrootDir$srcDir\n";
    &RecCreateDir("$chrootDir$srcDir",0755);


    @libs=();
    opendir (DIR,$srcDir) or die "Can't open dir $dir: $!\n";
    while ( $fullName = readdir(DIR)){
      $srcPath="$srcDir/$fullName";
      unless ( -f $srcPath || -l $srcPath ){
	next;
      }
      ($name,$ext)=(&parsePath($fullName))[1,2];
      unless ($ext eq 'so'){
	next;
      }
      push (@libs,$fullName);
    }
    closedir(DIR);

#
# list of libraries dirs
#
    unless(exists($ldDirs{$srcDir})){
      $npp++;
      $ldDirs{$srcDir}=$npp;
    }
#
#
#

#
# find name
#
    @names = @{$so{$srcDir}};

    foreach $fullName (@libs) {
      $srcPath="$srcDir/$fullName";
      ($name,$ext)=(&parsePath($fullName))[1,2];

      @links=();
      foreach $dstName (@names) {
	if ($name eq $dstName){

	  $dstPath = "$chrootDir$srcDir/$fullName";

	  if (-l $srcPath ){
	    $srcLink=readlink($srcPath);
	    unless ($srcLink =~ /^\//){
	      push (@links,$srcLink);
	    }
	  }
	
	  &doCopy ($srcPath, $dstPath, 'so', "$chrootDir$srcDir");

	  last;
	}
      }

#
# process of symbolic links
#
      while (@links){
	@forAdd=();
	foreach $link (@links){
	  $dstPath="$chrootDir$srcDir/$link";
	  $srcPath="$srcDir/$link";
	  if ( -l $srcPath){
	    $srcLink=readlink($srcPath);
	    unless ($srcLink =~ /^\//){
	      push (@forAdd,$srcLink);
	    }
	  }

	  &doCopy($srcPath,$dstPath,'so',"$chrootDir$srcDir");

	}
	@links=@forAdd;
      }
    }
  }

#
# create /etc/ld.so.conf
#
  if (-x $bin_ldconfig){

    my $ldSoConf="$chrootDir/etc/ld.so.conf";

    print "Create $ldSoConf\n";

    if(open(LDCONF,"> $ldSoConf")){
      my %revDirs = reverse %ldDirs;
      foreach  $npp (sort keys %revDirs){
	$dir = $revDirs{$npp};
	if ($dir eq '/lib' || $dir eq '/usr/lib'){
	  next;
	}
	print LDCONF $dir,"\n";
      }
      close(LDCONF);
    }
    if (-T $ldSoConf){

      chmod 0644,$ldSoConf;

      my $ldSoCache="$chrootDir/etc/ld.so.cache";
      print "Create $ldSoCache\n";

      my $cmd="$bin_ldconfig -f /etc/ld.so.conf  -r $chrootDir";
      unless (system ($cmd)==0){
	print STDERR "Error executing command: $cmd\n$!\n";
      }
    }
  }else{
    &doCopy ('/etc/ld.so.conf',"$chrootDir/etc/ld.so.conf",'',"$chrootDir/etc");
    &doCopy ('/etc/ld.so.cache',"$chrootDir/etc/ld.so.cache",'',"$chrootDir/etc");
  }


  foreach $dir (keys %prog){
    $dstDir=$chrootDir.$dir;
    print "Create dir: $dstDir\n";
    &RecCreateDir($dstDir,0755);
  
    @names = @{$prog{$dir}};
    while (@names){
      @forAdd=();
      foreach $name (@names){
	$srcPath="$dir/$name";
	unless ( -x $srcPath ){
	  next;
	}
	if (-l $srcPath){
	  $srcLink=readlink($srcPath);
	  unless ($srcLink =~ /^\//){
	    push (@forAdd,$srcLink);
	  }
	}
	$dstPath = "$dstDir/$name";

	&doCopy ($srcPath,$dstPath,'',$dstDir);

      }
      @names=@forAdd;
    }
  }

  return $modeDo?1:0;

}
#
# subroutines
#

sub doCopy {
  my ($srcPath,$dstPath,$ext,$dstDir)=@_;
  my $fullName,$mode;
  unless($dstDir){
    ($fullName,$dstDir,$ext)=fileparse($path);
  }
  unless (!$force_rewrite && (-f $dstPath || -l $dstPath) ){
    if ($ext=~/so/){
      print "copy library: $srcPath -> $dstPath\n";
    }else{
      print "copy file: $srcPath -> $dstPath\n";
    }
    if (-l $srcPath && -x $bin_cp ){
      system("$bin_cp $srcPath $dstDir -df");
    }else{
      copy($srcPath,$dstPath);
      $mode=(stat($srcPath))[2] & 07777;
      chmod($mode,$dstPath);
    }
  }
}

sub createTree {
  my $root = shift;
  foreach my $dir (@_){
    &createDir("$root$dir");
  }
}

sub parsePath {
  my $path=shift;
  my $dir,$fullName,$name,$ext;
  ($fullName,$dir,$ext)=fileparse($path);

  while ($dir =~ /(.*)\/+$/ ){
    $dir=$1;
  }

  if ($fullName =~ /^([^\.]+)(.*)$/){
    $name = $1;
    $ext = $2;
    if ($name =~ /^(.*)-[\d\.]+$/){
      $name=$1;
    }
    if ($ext=~/\.so/){
      $ext='so';
    }else{
      $ext='';
    }
  }else{
    $name = $fullName;
  }

  return ($dir,$name,$ext,$fullName);
}


sub ltext {
  my $text_name = shift;
  if (!%lang_text) {
    my $langFile = "$installDir/admin/languages/$language/scripts.local";
    do $langFile;
  }
  if (!defined($lang_text{$text_name})) {
    return "Sorry, text $text_name not found!";
  }
  else {
    my @VAR = @_;
    my $text = $lang_text{$text_name};
    $text =~ s/\$VAR\[(\d+)\]/$VAR[$1]/g;
    if ($text) {
      return $text;
    }
    else {
      return "Sorry, localizing text $text_name failed!";
    }
  }
}

1;

__END__
=head1 NAME

chroot_install.pl - the tool of Confixx. Version 3.0

=head1 SYNOPSIS

chroot_install.pl <programs> [options]

B<programms>:     the list of programs for the chroot execution

options:          --root E<verbar> --ldd E<verbar> --force E<verbar> --help E<verbar> --version

=head1 OPTIONS

=over 4

=item E<lt>programsE<gt> 

the list of full paths to program

=item B<-r|--root>

path to root folder for chroot

=item B<--ldd>

path to ldd-command

output of ldd must be 

<name of library> => (<full path to library>)

=item B<-f|--force>

rewrite destination. Default skip if exists.

=item B<--help>

full documentation

=back

=head1 USAGE

example:
chroot_install.pl /usr/bin/mysql -r=/home/confixx/root

=cur
