#! /usr/bin/perl
#
# kinst v1.1 - todd j. derr <tjd@barefoot.org>
#
# usage: kinst [-d path] [-i path] [-z file] [-m file] [-v version] [-f] [tag]
#
#	-d:  path to top level kernel source dir
#	-i:  destination for installation of image and map files
#	-z:  full filename for zImage
#	-m:  full filename for System.map
#	-v:  kernel version: v.p.s (ie. '1.3.3')
#	-f:  force 'yes' answers to all questions
#	tag: extra tag to use on installed image
#
# installs kernel images in boot dir as $ZIMAGE.v.p.s[.extra_tag]
# v1.1: now installs System.maps similar to zImages.

# Configuration section: fs dependent stuff (defaults)
$INSTALLDIR="/boot";		# destination for install (-i)
$INST_IMG="";			# image destination relative to INSTALLDIR
$INST_MAP="";			# map destination relative to INSTALLDIR

$GENLILO="/sbin/genlilo";	# path to genlilo
$CP="/bin/cp";			# path to cp
$INSTALL_MAP="gzip";		# can be 'yes', 'no', or 'gzip'.
$GZIP="/usr/bin/gzip";		# path to gzip (only used if INSTALL_MAP=gzip)

# please note that specifying -z or -m will override these.
# i.e. -z and -m are NOT relative to these directories.

$KERNEL_SRC="/usr/src/linux";	# path to kernel sources (-d)

$ZIMAGE_DIR="arch/i386/boot";	# path to zImage relative to $KERNEL_SRC
$ZIMAGE="zImage";		# filename of kernel file

$SYSMAP_DIR="";			# path to System.map relative to $KERNEL_SRC
$SYSTEM_MAP="System.map";	# filename of System.map

# --- You shouldn't need to change anything below here ---

# check for command-line options.
$prog=&basename($0);
require "getopts.pl";

&Getopts('d:z:m:v:i:fn');

if($opt_d) { $KERNEL_SRC="$opt_d"; }
if($opt_i) { $INSTALLDIR="$opt_i"; }
if($opt_v) { $version="$opt_v"; }
if($opt_z) { $PATH_ZIMAGE="$opt_z"; }
if($opt_m) { $PATH_MAP="$opt_m"; }
if($opt_f) { $force=1; }
if($opt_n) { $fake_it=1; }	# like make -n, undocumented for debugging.

# don't change these, change values above or from the command line.
$PATH_MAKEFILE="$KERNEL_SRC/Makefile";
$IMGDIR="$INSTALLDIR/$INST_IMG";
$MAPDIR="$INSTALLDIR/$INST_MAP";

if($PATH_ZIMAGE)
{
	$ZIMAGE=&basename($PATH_ZIMAGE);
}
else
{
	$PATH_ZIMAGE="$KERNEL_SRC/$ZIMAGE_DIR/$ZIMAGE";
}

if($PATH_MAP)
{
	$SYSTEM_MAP=&basename($PATH_MAP);
}
else
{
	$PATH_MAP="$KERNEL_SRC/$SYSMAP_DIR/$SYSTEM_MAP";
}

&check_paths && &kdie;

# get version from makefile unless specified on the command line
if (!$version)
{
	$version=&get_version("$PATH_MAKEFILE");
}

if($#ARGV == 0) { $version="$version.$ARGV[0]"; } # add tag


# do the actual installing
print "$prog: installing kernel version $version.\n";

$ZIMAGE_DEST="$IMGDIR/$ZIMAGE.$version";
&safe_copy("$PATH_ZIMAGE","$ZIMAGE_DEST") && &kdie;

unless("$INSTALL_MAP" eq 'no')
{
	$MAP_DEST="$MAPDIR/$SYSTEM_MAP.$version";

	if(&safe_copy("$PATH_MAP","$MAP_DEST"))
	{
		warn("$prog: removing $ZIMAGE_DEST.\n");
		unlink($ZIMAGE_DEST) unless ($fake_it);
		&kdie;
	}

	if("$INSTALL_MAP" eq 'gzip')
	{
		$GZIP="$GZIP -f" if $force;

		if(&do_system("$GZIP -9 $MAP_DEST"))
		{
			warn("$prog: failed to gzip $MAP_DEST!\n");
		}
	}
}


# run genlilo to generate new lilo configuration.
if(&do_system("$GENLILO -d $IMGDIR -z $ZIMAGE"))
{
	warn("$prog: error running $GENLILO!\n");
	&kdie;
}

exit 0;
# ------ end of main -------

# just dies.
sub kdie { die("$prog: exiting...\n"); }

# basename function
sub basename { local($n)=@_; $n =~ s#.*/##g; return $n; }

# used to implement -n
sub do_system
{
	if($fake_it)
	{
		print("$prog: system @_\n");
		return 0;
	}
	else
	{
		system(@_);
	}
}

# gets the version number from the path specified and returns it as a string.

sub get_version
{
	local($makefile)=@_;
	local($vs,$pls,$sls,$vers,$pl,$sl,$v);

	if(!open(M,"$makefile"))
	{
		warn("$prog: open $makefile: $!\n");
		&kdie;
	}

	$vs=<M>; $pls=<M>; $sls=<M>;
	close(M);

	($vers)=($vs =~ /VERSION = (\d+)/);
	($pl)=($pls =~ /PATCHLEVEL = (\d+)/);
	($sl)=($sls =~ /SUBLEVEL = (\d+)/);

	$v="$vers.$pl.$sl";

	return $v;
}

# copies src to dest, returns 0 on success, non-zero on failure.

sub safe_copy
{
	local($src,$dest)=@_;

	if( ! -f $src ) 
	{
		warn("$prog: can't find file $src!\n");
		return 1;
	}

	if( -f "$dest" && !$force && !$fake_it )
	{
		print "$prog: $dest exists, overwrite [y/N]? ";
		$_=<STDIN>;
		return 2 unless /[Yy]/;
	}
	
	if(&do_system("$CP $src $dest"))
	{
		warn("$prog: error copying $src to $dest!\n");
		unlink("$dest") unless ($fake_it);
		return 3;
	}

	return 0; 
}

# checks to make sure that things are configured properly.
# returns 0 if all looks ok, nonzero otherwise.

sub check_paths
{
	local($ret);
	$ret=
		&check_dir("$IMGDIR") +
		&check_exec("$GENLILO") +
		&check_exec("$CP");

	unless("$INSTALL_MAP" eq 'no')
	{
		$ret += 
			&check_dir("$MAPDIR") +
			&check_dir("$KERNEL_SRC/$SYSMAP_DIR");

		if("$INSTALL_MAP" eq 'gzip')
		{
			$ret += &check_exec("$GZIP");
		}
	}

	return $ret;
}

sub check_dir
{
	local($dir)=@_;

	if ( -d $dir )
	{
		return 0;
	}
	else
	{
		warn("$prog: directory $dir does not exist.\n");
		return 1;
	}
}

sub check_exec
{
	local($prog)=@_;

	if ( -x $prog )
	{
		return 0;
	}
	else
	{
		warn("$prog: can't find executable $prog.\n");
		return 1;
	}
}
