#!/usr/bin/perl

require 5.003;

###
###
##
##	Program:	LTSP Application Copy (ltsacp)
##	Author:		Rob Lucke (Rob_Lucke@hp.com)
##	Version:	1.0.1 
##	Date:		20020402
##
##	Description:
##			This script will determine the list of shared library 
##			dependencies for an application passed on the command
##			line and will optionally copy the application and its
##			shared libraries to the LTSP root directory.
##
##
##	Options:	--copy, -c		Copy application and libraries to
##						the LTSP root.
##
##			--help, -h, -?		Obtain usage information.
##
##			--link, -l		Create hard links instead of copying
##
##			--root, -r		Specify LTSP root (other than the
##						default location.
##
##			--test, -t		Go through the motions, but don't really
##						do anything.
##
##			--verbose, -v		Output verbose messages about actions
##						being taken.
##
##
##	$Log: ltsacp,v $
##	Revision 1.1  2002/04/09 15:11:08  rlucke
##	Corrected several bugs, thanks to Christian (adkicki@yahoo.de) who found them.
##
# Revision 1.0  2002/04/03  11:44:03  11:44:03  rlucke (Rob Lucke)
# Initial revision
# 
##
###
###
##------------------------------------------------------------------------------
## Global variables and arrays that can be pre-initialized
##------------------------------------------------------------------------------

( $prog ) = ( $0 =~ m|([^/]+)$| );
						# Program ID, needs to be first!
$apptoprocess	= undef;
						# Name of the application to copy
$dolink		= undef;
						# TRUE if want to link files instead of copy
$dotest		= undef;
						# TRUE if want to copy files
$lddprog	= "/usr/bin/ldd";
						# Dependency lister
$ltspdir	= "/opt/ltsp/i386";
						# Default LTSP location directory
$operation	= "Copy";
						# Value is either "link" or "copy"
$rcsheader	= '$Header: /home/rlucke/Tools/Ltsp/RCS/ltsacp,v 1.1 2002/04/09 15:11:08 rlucke Exp rlucke $';
						# RCS Version information
$rcsstr		= "\t$prog: $rcsheader - Rob_Lucke@hp.com";
						# Version output string
$rootdir	= $ltspdir;
						# Directory root for our actions
$user        	= $ENV{LOGNAME};
						# Current user name
$verbosity	= 0;
						# Default verbosity level
$versionprog 	= "1.0.0";
						# Current program version level
$versionstr  	= "$prog: Version $versionprog - Copy or Link Application+Shared Libs to LTSP Root";
						# Complete program version string

##------------------------------------------------------------------------------
## Subroutines
##------------------------------------------------------------------------------

##
##
#       This routine kills the program, outputting a message and information
#       about where the error occurred.  We can output a consistent message
#       format by funneling all of the error messages through this routine.
#
sub DIE {
        my ( $message, $line ) = @_;
        my ( $prog ) = ( $0 =~ m|([^/]+)$| );
        die( "\n$prog: $message \n\t\t Terminating... Closest source line number is $line.\n\n" );
}

##
##
#       This routine outputs usage information in case help is requested or if
#       there is some problem with the invocation.
#
sub Usage {

		
        my $prog;

        ($prog) = ( $0 =~ m|([^/]+)$| );

#        1         2         3         4         5         6         7         8
#2345678901234567890123456789012345678901234567890123456789012345678901234567890
        print <<EOF;

Usage:	\"$prog [OPTIONS] <program_file>\"

	\"<program_file>\" should be a fully-qualified path name to avoid any
possible ambiguities (like /usr/X11R6/bin/xhost /usr/bin/X11/xhost) that might
arise from searching the PATH variable -- which \"$prog\" doesn't.

	\"$prog\" will determine and list the shared library dependencies for 
the application passed on the command line, and will optionally copy the 
application and the shared libraries to the LTSP root directory, making them 
available for the diskless client.  Only files that do not exist in the LTSP 
root environment will be copied.  Possible OPTIONS are:

	--help, -h, -?			Print this help message.

	--link, -l			Create hard links in the LTSP root
					instead of copies.

	--root, -r			Specify the LTSP root directory, if 
					other than the default location.

	--test, -t			Test operations only, do not perform
					them.

	--verbose, -v			Output verbose messages about actions.

You must be the \"root\" user (or equivalent) to use $prog, determined by 
your user name and UID values.

EOF
	exit(1);
}


 
##------------------------------------------------------------------------------
## Main processing
##------------------------------------------------------------------------------

( $prog )      = ( $0 =~ m|([^/]+)$| );

###
##
##      Argument processing
##
###

while ( $_ = $ARGV[0], /^-/ ) {
        shift;
        last if /^--$/;
        if ( /^-l/ || /^--link/   ) {
                $dolink    = "true";
		$operation = "Link";
                next;
        }
        if ( /^-r/ || /^--root/    ) {
		$rootdir = $ARGV[0];
                shift;
                next;
        }
        if ( /^-t/ || /^--test/   ) {
                $dotest    = "true";
                $verbosity++;		# Show the operations
                next;
        }
        if ( /^-v/ || /^--verbose/ ) {
                $verbosity++;		# Each switch increments
                next;
        }
        if ( /^-?/ || /^-h/ || /^--help/ ) {
                Usage();
                next;
	}
	Usage();
}
$apptoprocess = $_;
if ( ! length $apptoprocess ) {
	Usage();
}

###
##
##      Put out initial version string
##
###

($verbosity >= 1 )  && print "\n$versionstr\n";
($verbosity >= 2 )  && print "$rcsstr\n";
($verbosity >= 1 )  && print "\n";

$line = __LINE__;

#  Check the conditions under which we will allow execution.
#
( $user eq "root" )     	||
( $>    == 0    )       	||
        DIE( "You must run $prog as the root user or equivalent.", $line );

( -f $apptoprocess )		||	
	DIE( "The program file \"$apptoprocess\" does not exist.", $line );

( -d $rootdir )			||
	DIE( "The LTSP root, \"$rootdir\" does not exist.", $line );

( -x $lddprog ) 		||
	DIE( "Unable to locate \"$lddprog\" for dependency list.", $line );

open( LDD, "$lddprog $apptoprocess |" ) ||
        DIE( "Unable to create \"$lddprog\" child process.", $line );

$line           = __LINE__;
$filestoprocess = {};

#  Use the "ldd" command to list all of the shared libraries used by the
#  application being processed.  Add any libraries that do not exist in the
#  LTSP root to the hash for processing.
#
while ( <LDD> ) {

	/not a dynamic executable/ &&
		DIE( "\"$apptoprocess\" is not dynamically linked, use regular \"cp\"", $line );

	next if /ld.so/ || /ld-linux/;		# Better already be there ...

	($library, $separator, $fullpath, $address) = split;

	if ( $verbosity >= 1 ) {
		print "$prog: Library \"$rootdir$fullpath\" ";
	}

	if ( -f "$rootdir$fullpath" ) {		# Already in the LTSP root?
		( $verbosity >= 1 ) && print "already exists.\n";
	}
	else {
		( $verbosity >= 1 ) && print "does not exist.\n";
		$filestoprocess->{$library} = $fullpath;
	}

}

$line = __LINE__;

close( LDD ) ||
	DIE( "Unable to close pipe to \"$lddprog\" child process.", $line );

($verbosity >= 1 ) && print "\n";

#  The application is already in the LTSP root directory.
#
if ( -e "$rootdir$apptoprocess" ) {
	( $verbosity >= 1 ) &&
		print "$prog: Application \"$rootdir$apptoprocess\" already exists.\n\n";
	exit ( 1 );
}

#  If the application does not currently exist in the LTSP root, then 
#  copy/link the application and the shared libraries contained in the
#  $filestoprocess hash.  Add the application to the list of libraries, 
#  so that they can all be processed at the same time.  Since we don't use
#  the hash key for anything, use a special one for the app.
#
$filestoprocess->{"AppToCopy"} = $apptoprocess;

foreach $targetfile ( sort keys %$filestoprocess ) {

	if ($verbosity >= 1 ) {
		print "$prog: $operation $filestoprocess->{$targetfile}     ";
		print 		"->  $rootdir$filestoprocess->{$targetfile} ...";
	}

	#  If not performing a "test" run, then we actually will either link or
	#  copy the application and associated libaries contained in the hash.
	#
	if ( not $dotest ) {

		#  Ensure that the library/application path already exists in
		#  the LTSP root directory.
		#
		( $filepath ) =  ( $filestoprocess->{$targetfile} =~ /(\/.*\/)+/ );
		( not -d $filepath.$rootdir ) &&
			`mkdir --parents $rootdir$filepath`;

		#  Perform the operation now, checking for error conditions.
		#
		if ( $operation eq "Copy" ) {
			`cp $filestoprocess->{$targetfile} $rootdir$filepath`;	
			$error = $?;	
		}
		if ( $operation eq "Link" ) {
			`ln $filestoprocess->{$targetfile} $rootdir$filepath`;
			$error = $?;
		}
		if ( $error ) {
			print "!FAILED! Error $?.";
		}
		else {
			print " Done.";
		}

	}
	else {
		print " Test Only.";
	}
	($verbosity >= 1 ) && print "\n";
}

($verbosity >= 1 ) && print "\n";

exit( 0 );

###
###
