#! /bin/bash

##
## ybin (YaBoot INstaller) installs/updates the yaboot bootloader.
## Copyright (C) 2000 Ethan Benson
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
##

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PRG=`basename $0`
CONF=/etc/ybin.conf
ERR="Error in $CONF"
TMP=/tmp

## check for existence of a configuration file, and make sure we have
## read permission.

confexist()
{
    if [ ! -f "$CONF" ] ; then
	echo 1>&2 "$PRG: $CONF: File not found"
	return 1
    elif [ ! -r "$CONF" ] ; then
	echo 1>&2 "$PRG: $CONF: Permission denied"
	return 1
    else 
	return 0
    fi
}

## check to make sure the configuration file is sane and correct.
## maybe this is an insane ammount of error checking, but I want to
## make sure (hopfully) nothing unexpected ever happens.  and i just
## like useful errors from programs.  every error just marks an error
## variable so we give the user as much info as possible before we
## abandon ship.

checkconf()
{
    if [ ! -e "$boot" ] ; then
	echo 1>&2 "$PRG: $boot: File not found"
	CONFERR=1
    elif [ ! -b "$boot" -a ! -f "$boot" ] ; then
	    echo 1>&2 $"$PRG: $boot: Not a regular file or block device"
	    CONFERR=1
    elif [ ! -w "$boot" -o ! -r "$boot" ] ; then
	echo 1>&2 "$PRG: $boot: Permission denied"
	CONFERR=1
    fi

    if [ ! -f "$bootfile" ] ; then
	echo 1>&2 "$PRG: $bootfile: File not found"
	CONFERR=1
    elif [ ! -r "$bootfile" ] ; then
	echo 1>&2 "$PRG: $bootfile: Permission denied"
	CONFERR=1
    fi

    if [ ! -f "$bootconf" ] ; then
	echo 1>&2 "$PRG: $bootconf: File not found"
	CONFERR=1
    elif [ ! -r "$bootconf" ] ; then
	echo 1>&2 "$PRG: $bootconf: Permission denied"
	CONFERR=1
    fi

    if [ "$wrapper" ] ; then
	if [ ! -f "$wrapper" ] ; then
	    echo 1>&2 "$PRG: $wrapper: File not found"
	    CONFERR=1
	elif [ ! -r "$wrapper" ] ; then
	    echo 1>&2 "$PRG: $wrapper: Permission denied"
	    CONFERR=1
	fi
    fi

    case "$usemount" in 
	yes|no)
	  ;;
	*)
	  echo 1>&2 "$PRG: $ERR: \`usemount' must be either \`yes' or \`no'"
	  CONFERR=1
	  ;;
    esac

    case "$fstype" in
	hfs|msdos)
	 ;;
	*)
	 echo 1>&2 "$PRG: $ERR: \`fstype' must be either \`hfs' or \`msdos'"
	 CONFERR=1
	 ;;
    esac

    ## if we are not using HFS filesystems we don't care about HFS
    ## specific config options.

    if [ "$fstype" = hfs ] ; then
	if [ `printf $hfstype | wc -c` != 4 ] ; then
	    echo 1>&2 "$PRG: $ERR: \`hfstype' must be 4 characters"
	    CONFERR=1
	fi
	
	if [ `printf $hfscreator | wc -c` != 4 ] ; then
	    echo 1>&2 "$PRG: $ERR: \`hfscreator' must be 4 characters"
	    CONFERR=1
	fi

	if [ "$hide" ] ; then
	    case "$hide" in 
	       yes|no)
		;;
	       *)
		echo 1>&2 "$PRG: $ERR: \`hide' must be either \`yes' or \`no'"
		CONFERR=1
	        ;;
	    esac
	fi
	
	case "$bless" in 
	    yes|no)
	     ;;
	    *)
	     echo 1>&2 "$PRG: $ERR: \`bless' must be either \`yes' or \`no'"
	     CONFERR=1
	     ;;
	esac

	case "$kludge" in 
	    yes|no)
	     ;;
	    *)
	     echo 1>&2 "$PRG: $ERR: \`kludge' must be either \`yes' or \`no'"
	     CONFERR=1
	     ;;
	esac

	if [ "$kludge" = yes ] ; then
	    BTFILE=`basename $bootfile`
	    CFFILE=`basename $bootconf`
	    if [ ! -f $kludgedir/$BTFILE -o ! -f $kludgedir/$CFFILE ] ; then 
		echo 1>&2 "$PRG: Error kludge files missing, see README file"
		CONFERR=1
	    fi
	fi
    fi

    ## protect works for both dosfs and hfs

    if [ "$protect" ] ; then
	case "$protect" in 
	    yes|no)
	     ;;
	    *)
	     echo 1>&2 "$PRG: $ERR: \`protect' must be either \`yes' or \`no'"
	     CONFERR=1
	     ;;
	esac
    fi

    if [ "$CONFERR" = 1 ] ; then 
	return 1
    else 
	return 0
    fi
}


## make sure the hfsutils we need are installed and accessable.

checkhfsutils()
{
    which hmount > /dev/null && [ -x `which hmount` ]
    if [ $? != 0 ] ; then 
	return 1
    fi

    which humount > /dev/null && [ -x `which humount` ]
    if [ $? != 0 ] ; then
	return 1
    fi

    which hcopy > /dev/null && [ -x `which hcopy` ]
    if [ $? != 0 ] ; then
	return 1
    fi

    which hattrib > /dev/null && [ -x `which hattrib` ]
    if [ $? != 0 ] ; then 
	return 1
    fi

    return 0
}

## install using userspace utilities rather then kernel filesytem
## support.  this would be hfsutils and mtools for dos, right now its
## only hfsutils since i am too lazy to figure out how mtools works.
## dosfs kernel support is almost without question avaiable however.

util_install()
{
    ## this is just to neaten up the code below.  yaboot requires the
    ## yaboot.conf (or yaboot.cnf) filename so we just hard code that
    ## here.

    BTFILE=`basename $bootfile`
    CFFILE=yaboot.conf

    ## if there is an OF wrapper to install we will give it the
    ## hfstype (should be "tbxi") and give yaboot type "boot".  This
    ## way the right file gets loaded by OF.  Might make all filetypes
    ## configurable, but its really not important.

    if [ "$wrapper" ] ; then
	BTTYPE=boot
    else
	BTTYPE=$hfstype
    fi

    if [ "$wrapper" ] ; then
	WRAP=`basename $wrapper`
    fi

    if [ "$fstype" = hfs ] ; then
    
	if [ "$protect" = yes ] ; then
	    LOCK="+l"
	fi
	
	if [ "$hide" = yes ] ; then
	    INVISIBLE="+i"
	fi

	## make sure the device is not mounted as a filesystem before
	## we start mucking with it directly.

	mount | grep -w $boot > /dev/null
	if [ $? = 0 ] ; then
	    echo 1>&2 "$PRG: $boot appears to be mounted! aborting."
	    return 1
	fi	    

	## hmount is really more of a way to make sure we have a valid HFS
	## filesystem before proceding, it does not really do anything else.

	hmount $boot > /dev/null
	if [ $? != 0 ] ; then
	    echo 1>&2 "$PRG: An error occured trying to access $boot as HFS"
	    return 1
	fi
	
	hcopy -r $bootfile : 
	if [ $? != 0 ] ; then 
	    echo 1>&2 "$PRG: An error occured while copying files to $boot"
	    return 1
	fi
	
	hcopy -r $bootconf :
	if [ $? != 0 ] ; then 
	    echo 1>&2 "$PRG: An error occured while copying files to $boot"
	    return 1
	fi

	if [ "$wrapper" ] ; then
	    hcopy -r $wrapper :
	    if [ $? != 0 ] ; then 
	       echo 1>&2 "$PRG: An error occured while copying files to $boot"
	       return 1
	    fi
	fi

	## set all files attributes, if a wrapper exists it gets the
	## configured hfstype instead of yaboot (should be "tbxi") so
	## it gets booted by OF.

	hattrib -t "$BTTYPE" -c "$hfscreator" $INVISIBLE $LOCK :"$BTFILE"
	if [ $? != 0 ] ; then 
	    echo 1>&2 "$PRG: Warning: error setting attributes on $BTFILE"
	    echo 1>&2 "$PRG: This is probably bad but we'll ignore it"
	fi
	    
	hattrib -t "conf" -c "$hfscreator" $INVISIBLE $LOCK :"$CFFILE"
	if [ $? != 0 ] ; then
	    echo 1>&2 "$PRG: Warning: error setting attributes on $CFFILE"
	    echo 1>&2 "$PRG: This is probably unimportant so we'll ignore it"
	fi
	
	if [ "$wrapper" ] ; then
	    hattrib -t "$hfstype" -c "$hfscreator" $INVISIBLE $LOCK :"$WRAP"
	    if [ $? != 0 ] ; then
		echo 1>&2 "$PRG: Warning: error setting attributes on $WRAP"
		echo 1>&2 "$PRG: This is probably bad but we'll ignore it."
	    fi
	fi

	## bless the root directory so OF will find the boot file

	if [ "$bless" = yes ] ; then
	    hattrib -b :
	    if [ $? != 0 ] ; then
		echo 1>&2 "$PRG: Warning: error blessing $boot"
		echo 1>&2 "$PRG: This is probably bad but we'll ignore it"
	    fi
	fi

	## clean up the ~/.hcwd file hmount creates
	humount $boot > /dev/null
	sync ; sync

    else 
	echo 1>&2 "$PRG: mtools support is not implemented"
	echo 1>&2 "$PRG: change \`usemount' to \`yes' in $CONF"
	return 1
    fi

    return 0
}


## Use kernel filesytem drivers to mount the bootstrap partition like
## any other filesystem and copy the files there with standard un*x
## utilities.  since normally root is only allowed to mount
## filesystems we yell at normal users.  (if/when yaboot supports lilo
## style password protection, only root should be allowed to access
## the bootstrap partition anyway. this is why we make sure nothing is
## readable by anyone but root)

mnt_install()
{
    if [ `id -u` != 0 ] ; then
	echo 1>&2 "$PRG: \`usemount' is \`yes' so we can only be run by root."
	return 1
    fi

    ## this is again to keep things slightly neater.. $BROKEN is used
    ## to fix the yaboot.conf filename that gets ruined when we copy
    ## it to a dosfs.

    BTFILE=`basename $bootfile`
    BROKEN=yaboot.con
  
    if [ "$fstype" = msdos ] ; then
	CFFILE=yaboot.cnf
    else
	CFFILE=yaboot.conf
    fi

    if [ "$wrapper" ] ; then
	WRAP=`basename $wrapper`
    fi

    ## we can even create bootstrap filesystem images directly if you
    ## ever wanted too.

    if [ -f "$boot" ] ; then
	loop=",loop"
    fi

    if [ -e $TMP/bootstrap.$$ ] ; then
	echo 1>&2 "$PRG: $TMP/bootstrap.$$ exists, aborting."
	return 1
    fi

    mkdir -m 700 $TMP/bootstrap.$$
    if [ $? != 0 ] ; then
	echo 1>&2 "$PRG: Could not create mountpoint directory, aborting."
	return 1
    fi

    mount -t $fstype -o rw,umask=077$loop $boot $TMP/bootstrap.$$
    if [ $? != 0 ] ; then 
	echo 1>&2 "$PRG: An error occured mounting $boot"
	rmdir $TMP/bootstrap.$$
	return 1
    fi

    cp -f $bootfile $bootconf $wrapper $TMP/bootstrap.$$/    
    if [ $? != 0 ] ; then 
	echo 1>&2 "$PRG: An error occured while copying files to $boot"
	echo 1>&2 "$PRG: Attempting to umount $boot..."
	umount $TMP/bootstrap.$$
	if [ $? != 0 ] ; then
	    echo 1>&2 "$PRG: failed!"
	    return 1 
	fi

	rmdir $TMP/bootstrap.$$
	echo 1>&2 "$PRG: umount sucessfull"
	return 1
    fi

    if [ "$fstype" = msdos ] ; then
	mv -f $TMP/bootstrap.$$/$BROKEN $TMP/bootstrap.$$/$CFFILE
	    if [ $? != 0 ] ; then
		echo 1>&2 "$PRG: Error fixing `basename $bootconf` filename"
		echo 1>&2 "$PRG: This is probably bad but we'll ignore it"
	    fi
    fi

    ## I happened to notice if you overwrite a .finderinfo file on hfs
    ## with one another one the file its attached to gets the
    ## metadata.  this is still pretty ugly but interesting enough to
    ## toss in :)

    if [ "$kludge" = yes -a "$fstype" = hfs ] ; then
	cat $kludgedir/$BTFILE > $TMP/bootstrap.$$/.finderinfo/$BTFILE
	cat $kludgedir/$CFFILE > $TMP/bootstrap.$$/.finderinfo/$CFFILE
    fi

    if [ "$protect" = yes ] ; then 
	chmod a-w $TMP/bootstrap.$$/$BTFILE
	chmod a-w $TMP/bootstrap.$$/$CFFILE
	if [ "$wrapper" ] ; then
	    chmod a-w $TMP/bootstrap.$$/$WRAP
	fi
    fi

    sync ; sync
    umount $TMP/bootstrap.$$
    rmdir $TMP/bootstrap.$$
    return 0
}

confexist
if [ $? = 0 ] ; then
    source $CONF
else
    exit 1
fi

## force people to RTFM!!

if [ "$boot" = unconfigured ] ; then
    echo 1>&2 "$PRG: Oops you forgot to properly configure $PRG"
    echo 1>&2 "$PRG: Read the README for details on how to do that"
    exit 1
fi

checkconf
if [ $? != 0 ] ; then
    exit 1
fi

if [ "$usemount" = no ] ; then
    if [ "$fstype" = hfs ] ; then 
	checkhfsutils
	if [ $? != 0 ] ; then
	    echo 1>&2 "$PRG: hfsutils is not installed or cannot be found"
	    echo 1>&2 "$PRG: Try usemount=yes if `uname -sr` supports HFS"
	    exit 1
	fi
    fi

    util_install
    if [ $? != 0 ] ; then
	exit 1
    else 
	exit 0
    fi

else
    mnt_install
    if [ $? != 0 ] ; then
	exit 1
    fi
fi

exit 0
