#!/bin/sh
#
# $Id$
#
# A script to setup OpenAFS and configure it for MIT.

usage() {
    echo "Usage: $0 [ -k ]"
    echo "       -k   Install new binary kernel module for running kernel only"
    echo "            with no user interaction"
    exit 255
}

setuproot=/opt/mit-openafs-setup
opt_kmod=0
while getopts ":r:k" opt; do
  case $opt in
      r)
	  setuproot=$OPTARG
	  if [ ! -d $setuproot ]; then
	      echo "-r was passed non-existent directory"
	      exit 255
	  fi
	  ;;
      k) 
	  opt_kmod=1
	  ;;
      *)
	  usage
	  ;;
  esac
done

config=$setuproot/etc/setup.config
funcs=$setuproot/lib/functions.sh
logfile=$setuproot/log/setup.log

if [ -r $config ]; then
    source $config
else
    echo "ERROR: Unable to read $config"
    exit 1
fi

if [ -r $funcs ]; then
    source $funcs
else
    echo "ERROR: Unable to read $funcs"
    exit 1
fi


install_cur_kmod() {
    # This function attempts to find and install a binary RPM for the
    # currently running kernel only.  Yes, it duplicates a lot of stuff below
    # This can be called from RHGB, so we use our "output" function
    # instead of echo.  Nothing else should ever be called from RHGB.
    if [ -z "$iafsver" -o -z "$irpmrel" ]; then
	output "ERROR: iafsver or irpmrel not set in install_cur_kmod"
	return 1
    fi
    kver=`uname -r | sed -e 's/-/_/g'`
    karch=`uname -p`
    kpkg=kernel
    echo $kver | grep -q smp
    if [ $? = 0 ]; then
	kpkg=kernel-smp
    fi
    echo $kver | grep -q hugemem
    if [ $? = 0 ]; then
	kpkg=kernel-hugemem
    fi
    fname=openafs-${kpkg}-${iafsver}-${kver}_${irpmrel}.${karch}.rpm
    output -n "Checking for $fname..."
    checkforfile ${repository}/${rpmbasedir}/${fname}
    if [ $? = 0 ]; then
	output "found"
	getfile ${repository}/${rpmbasedir}/${fname}
	if [ $? != 0 ]; then
	    output "Failed to transfer file."
	    return 1
	fi
	rpm -iv --oldpackage $tmpspace/$fname
	if [ $? != 0 ]; then
	    output "Failed to install RPM."
	    return 1
	fi
    else
	output "not found"
	return 1
    fi
    return 0
}


install_kmod_pkg() {
    echo "Determining installed kernel package(s) and module availability:"
    # Get the list of kernels.  Can't do it all at once, since RPM will
    # spew "is not installed" messages, and set $? to nonzero.
    kernels=`rpm -q kernel`
    if [ $? != 0 ]; then
	# It's conceivable they could only have -smp or -hugemem installed
	kernels=
    fi
    smpkernels=`rpm -q kernel-smp`
    if [ $? = 0 ]; then
	kernels="$kernels $smpkernels"
    fi
    hmkernels=`rpm -q kernem-hugemem`
    if [ $? = 0 ]; then
	kernels="$kernels $hmkernels"
    fi
    kernelrpms=
    for k in $kernels; do
	suffix=
	echo $k | grep -q smp
	if [ $? = 0 ]; then
	    suffix=smp
	fi
	echo $k | grep -q hugemem
	if [ $? = 0 ]; then
	    suffix=hugemem
	fi
        # Don't assume i686
        # Yes, this has to be done for each one, since the same rpm can exist 
	# in different architectures (ie: x86_64 and i686 can coexist)
	karch=`rpm -q --qf "%{ARCH}" $k`
	pkgname=openafs-`rpm -q --qf "%{NAME}-${afsver}-%{VERSION}_%{RELEASE}\n" $k`${suffix}_${rpmrel}.${karch}
#    hmkernels=`rpm -q --qf "%{NAME}-${afsver}-%{VERSION}_%{RELEASE}\n" kernel-hugemem`
#	fname=openafs-${k}${suffix}_${rpmrel}.${karch}.rpm
	fname=$pkgname.rpm
	echo -n " $pkgname..."
	rpm -q --quiet $pkgname
	if [ $? = 0 ]; then
	    echo "installed"
	else 
	    checkforfile ${repository}/${rpmbasedir}/${fname}
	    if [ $? = 0 ]; then
		echo "found"
		kernelrpms="$kernelrpms $fname"
	    else
		echo "not found"
	    fi
	fi
    done
    for k in $kernelrpms; do
	getfile ${repository}/${rpmbasedir}/$k
    # You need --oldpackage since we're installing for all installed kernels
    # otherwise it'll only take the most recent.
    # And you can't do it in one bulk cmd, because it'll still check for
    # version conflicts and pick the newest
	if [ $? = 0 ]; then
	    rpm -iv --oldpackage $tmpspace/$k
	else
	    echo "Skipping $k"
	fi
    done
}

update() {
    [ -f /proc/mounts ] || echo "/proc/mounts doesn't exist, shouldn't happen."
    grep -qF /afs /proc/mounts
    if [ $? = 0 ]; then
	echo "I need to shutdown OpenAFS in order to continue."
	echo "Please make sure that neither you nor any other users on this"
	echo "machine are using any files in AFS, then continue with this update."
	echo -n "Continue? (y/n) "
	read answer
	case $answer in
	    y|Y)
		;;
	    n|N)
		return
		;;
	    *) 
		echo "Invalid answer, assuming 'n'"
		return
		;;
	esac
	if [ -x /etc/init.d/afs ]; then
	    /sbin/service afs stop
	elif [ -x /etc/init.d/openafs ]; then
	    /sbin/service openafs stop
	else
	    /sbin/service openafs-client stop
	fi
	if [ $? != 0 ]; then
	    echo "Failed to shutdown OpenAFS.  Please stop OpenAFS manually and try again."
	    errorout
	fi
    fi
    for rpm in $rpms; do
	fname=${rpm}-${afsver}-${osname}.${rpmrel}.${platform}.rpm
	getfile ${repository}/${rpmbasedir}/${fname}
	if [ $? != 0 ]; then
	    echo "Transfer errors occurred.  This could be the result of a"
	    echo "flaky network connection.  Please try again later."
	    errorout
	fi
    done
    if [ "$iafsver" = "1.2.11" ] || [ $iafsver = "1.2.10" ]; then
	# openafs-kernel depends on openafs = 1.2.11 and openafs-client
	# depends on openafs-kernel, so you lose.
	echo "Force-removing old openafs-kernel because of broken dependencies..."
	rpm -e --nodeps openafs-kernel >> $logfile 2>&1
    fi
    echo "Updating packages..."
    cd $tmpspace
    rpm -Uv *.rpm >> $logfile 2>&1
    if [ $? != 0 ]; then
	echo "An error occurred while installing the RPMs."
	echo "Consult $logfile for more information."
	errorout
    fi
    update_configs
    if [ -x /usr/bin/afslocker ] && [ ! -L /usr/bin/afslocker ]; then
	mv -f /usr/bin/afslocker /usr/bin/afslocker.rpmsave
	chmod 644 /usr/bin/afslocker.rpmsave
	ln -s /opt/mit-openafs-setup/bin/afslocker /usr/bin/afslocker
	echo "Moving existing afslocker to /usr/bin/afslocker.rpmsave"
    fi
    if [ -x /etc/init.d/afs-build-kmod ]; then
	echo "Deleting old afs-build-kmod service..."
	/sbin/chkconfig --del afs-build-kmod
	rm -f /etc/init.d/afs-build-kmod
    fi
    echo "Erasing old kernel module packages..."
    kpkgs="openafs-kernel \
          openafs-kernel-smp \
          openafs-kernel-hugemem"
    for pkg in $kpkgs; do
	rpm -q --quiet $pkg
	if [ $? = 0 ]; then
	    rpm -e --allmatches $pkg
	fi
    done
    install_kmod_pkg
    echo "Rebuilding module dependencies..."
    /sbin/depmod -a
    echo -n "Checking for a module for the running kernel..."
    /sbin/modprobe -nq openafs 2>/dev/null
    if [ $? != 0 ]; then
	echo "not found"
	echo "I could not find a pre-compiled openafs module for your kernel."
	echo "Attempting to run build script..."
	/opt/mit-openafs-setup/bin/build-module
	if [ $? != 0 ]; then
	    echo "The build script failed for some reason.  AFS is installed,"
	    echo "but will not be usable until you build and install a kernel"
	    echo "module.  You can do that be re-running this script at a later time."
	fi
    else
	echo "found"
    fi
    # Check if it's started in the current runlevel
    /sbin/chkconfig mit-openafs-check-kmod
    if [ $? != 0 ]; then
	install_init_script
    fi
    echo "OpenAFS has now been updated.  You should reboot your machine."

    if [ -d "$tmpspace" ]; then
	rm -rf $tmpspace
    fi
    
    exit 0    

}

update_kmod() {
    install_kmod_pkg
    echo -n "Checking for a module for the running kernel (`uname -r`)..."
    # We don't need to run depmod here, the RPMs do it in %post
    /sbin/modprobe -nq openafs 2>/dev/null
    if [ $? != 0 ]; then
	echo "not found"
	echo "You do not have a module for the currently running kernel."
	echo -n "Would you like to build one now? (y/n) "
	read answer
	case $answer in 
	    y|Y)
		/opt/mit-openafs-setup/bin/build-module
		;;
	    n|N)
		;;
	    *)
		echo "Invalid answer, assuming 'n'"
		;;
	esac
    else
	echo "found"
    fi
    echo -n "Would you like to build a module for another kernel version? (y/n)"
    read answer
    case $answer in 
	y|Y)
	    echo "Enter the kernel version, including any suffixes"
	    echo -n "such as 'smp' or 'hugemem': "
	    read kv
	    /opt/mit-openafs-setup/bin/build-module -k $kv
	    ;;
	n|N)
	    return
	    ;;
	*)
	    echo "Invalid answer, assuming 'n'"
	    return
	    ;;
    esac
}    

update_configs() {
    cfgfiles="CellAlias \
              CellServDB \
              SuidCells"
    for c in $cfgfiles; do
	update_afs_config $c
    done
}

install_init_script() {
    echo "OpenAFS includes an init script, which runs at boot time, and"
    echo "attempts to ensure that you have a suitable AFS kernel module."
    echo "If you choose not to have this script run at boot time, and you"
    echo "upgrade your kernel, you will need to re-run this setup script"
    echo "in order to fetch a new module."
    echo -n "Run script at boot time? (y/n) "
    read answer
    case $answer in 
	y|Y)
	    /sbin/chkconfig --levels 2345 mit-openafs-check-kmod on
	    ;;
	n|N)
	    ;;
	*)
	    echo "Invalid answer, assuming 'n'"
	    ;;
    esac
}

install() {
    for rpm in $rpms; do
	fname=${rpm}-${afsver}-${osname}.${rpmrel}.${platform}.rpm
	getfile ${repository}/${rpmbasedir}/${fname}
	if [ $? != 0 ]; then
	    echo "Transfer errors occurred.  This could be the result of a"
	    echo "flaky network connection.  Please try again later."
	    errorout
	fi
    done
    cd $tmpspace
    echo "Installing rpms..."
    rpm -iv *.rpm >> $logfile 2>&1
    if [ $? != 0 ]; then
	echo "An error occurred while installing the RPMs."
	echo "Consult $logfile for more information."
	errorout
    fi
    
# Do the config files here, in case building the kernel module fails, and the user restarts it
    echo "Updating AFS config files..."
    update_afs_config CellAlias
    set_cell_name

    install_kmod_pkg

    echo -n "Checking for a module for the running kernel..."
# We don't need to run depmod here, the RPMs do it in %post
    /sbin/modprobe -nq openafs 2>/dev/null
    if [ $? != 0 ]; then
	echo "not found"
	echo "I could not find a pre-compiled openafs module for your kernel."
	echo "Attempting to run build script..."
	/opt/mit-openafs-setup/bin/build-module
	if [ $? != 0 ]; then
	    echo "The build script failed for some reason.  AFS is installed,"
	    echo "but will not be usable until you build and install a kernel"
	    echo "module.  You can do that be re-running this script at a later time."
	fi
    else
	echo "found"
    fi
    
    install_init_script
    cat <<EOF

OpenAFS is now installed.  The OpenAFS service will start
automatically upon reboot, or you may start it manually with the
following command (as root):
   
    # /sbin/service openafs-client start

Note that this will not work if for some reason the kernel module
failed to build and you were not able to installed pre-built module.

If you ever upgrade your kernel, you will need a new OpenAFS kernel
module.  The best way to get the module is to run this setup script
again and follow the prompts.  However, we can also install a service
which will attempt to build a new copy of a kernel module if a
suitable one is not available.

EOF
    
    echo 
}


###################################################################
# Body




case `id` in
"uid=0("*)
  ;;
*)
  echo "You are not root.  This script must be run as root."
  errorout
  ;;
esac

if [ ! -x $curl ]; then
    echo "ERROR: $curl is not executable"
    errorout
fi

if [ -f /etc/athena/version ]; then
  echo "This script is not meant to be run on Athena machines."
  errorout
fi

if [ ! -f /etc/redhat-release ]; then
  echo "/etc/redhat-release doesn't exist.  Not continuing."
  errorout
fi

mkdir $tmpspace > /dev/null 2>&1
if [ $? != 0 ]; then
  echo "Failed to create temporary space at $tmpspace."
  errorout
fi

if [ -f $logfile ]; then
    mv -f $logfile $logfile.old
fi

echo "Beginning mit-openafs-setup at "`date` > $logfile
getosname
echo "osname=$osname" >> $logfile
if [ "$osname" == "unknown" ]; then
    echo "This installer will only work on Red Hat Enterprise Linux"
    echo "or Fedora Core.  Exiting..."
    errorout
fi
loadcfg
if [ $? -eq $EFETCHCFG ]; then
    echo "Could not fetch config file.  This may indicate a network problem"
    echo "or your operating system may not be supported."
    if [ $opt_kmod -eq 1 ]; then
	exit 1
    else
	errorout
    fi
fi
platform=`uname -i`
echo $archs | grep -q $platform
if [ $? != 0 ]; then
    echo "Sorry, OpenAFS is not available for the $platform platform."
    errorout
fi

iafsver=`rpm -q --qf "%{VERSION}" openafs`
if [ $? != 0 ]; then
    install
    exit 0
fi
    # The release of the packages is actually $osname.$rel
    # vs just $rel for the kernel module rpms
irpmrel=`rpm -q --qf "%{RELEASE}" openafs | sed -e "s/$osname\.//g"`
echo "Currently installed: OpenAFS $iafsver, RPM release $irpmrel"

if [ $opt_kmod -eq 1 ]; then
    install_cur_kmod
    exit $?
fi

echo "Checking for updates..."
update=0
if [ `expr "$iafsver" "<" "$afsver"` -eq 1 ]; then
    echo "A newer version of OpenAFS ($afsver) is available."
    update=1
elif [ "$afsver" == "$iafsver" ] && [ `expr "$irpmrel" "<" "$rpmrel"` -eq 1 ]; then
    echo "Your version of OpenAFS ($iafsver) is current, however"
    echo "there are new releases of the RPM packages (release $rpmrel)."
    echo "This should be a minor upgrade, which you are encouraged to take."
    update=1
else
    echo "Your installation of OpenAFS is up to date."
fi
if [ $update -eq 1 ]; then
    echo -n "Would you like to upgrade? (y/n) "
    read answer
    case $answer in
	y|Y)
	    update
	    ;;
	n|N)
	    ;;
	*) 
	    echo "Invalid answer, assuming 'n'"
	    ;;
    esac
fi

echo "What would you like to do?"

while true; do
    echo "1) Install a kernel module for an updated kernel"
    echo "2) Update OpenAFS configuration files"
    echo "3) Quit"
    echo
    echo -n "Enter a number: "
    read choice
    case $choice in
	1) 
	    if [ `expr "$iafsver" "<" "1.4"` -eq 1 ]; then
		echo "Sorry, this only works with OpenAFS 1.4 or newer."
		echo "You are running OpenAFS $iafsver"
	    else
		update_kmod
	    fi
	    echo -e "\nWhat would you like to do now?"
	    ;;
	2) 
	    update_configs
	    echo "Note that you will need to restart OpenAFS for these changes"
	    echo "to take effect."
	    echo -e "\nWhat would you like to do now?"
	    ;;
	3)
	    break
	    ;;
	*)
	    echo "Incorrect entry ($choice); try again"
	    echo "Please choose one of the following:"
	    ;;
    esac
done

if [ -d "$tmpspace" ]; then
    rm -rf $tmpspace
fi

exit 0    

    
