#! /bin/bash

# $Id: subroutines,v 1.42 2004/01/31 14:18:59 lange Exp $
#*********************************************************************
#
# subroutines -- useful subroutines for FAI
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2000-2003 by Thomas Lange, lange@informatik.uni-koeln.de
# Universitaet zu Koeln
#
#*********************************************************************
# 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.
# 
# A copy of the GNU General Public License is available as
# `/usr/share/common-licences/GPL' in the Debian GNU/Linux distribution
# or on the World Wide Web at http://www.gnu.org/copyleft/gpl.html.  You
# can also obtain it by writing to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#*********************************************************************

# source this file, then you have these function available in the shell

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
die() {

    # echo comment and exit installation
    echo "$@"
    exec bash
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
defnop() {

    # define given list of subroutine names as dummy function;
    # this will fake unknown commands

    local name
    for name in "$@";do
        eval "$name () { :;}"
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ifclass() {

    [ "$debug" ] && echo "Test if class $1 is in $classes"
    # test if a class is defined
    local cl
    local ret=1

    for cl in $classes; do
	[ x$cl = x$1 ] && ret=0 && break
    done
    [ "$debug" ] && echo "ifclass returns $ret"
    return $ret
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rwmount() {

    # remount partition read/write
    mount -o rw,remount $1
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
save_dmesg() {

    dmesg > $LOGDIR/dmesg.log
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
wait_for_jobs() {

    # can be an extern script
    # wait for running (background) jobs to finish (e.g. update-auctex-elisp)
    local i=0
    while jobsrunning; do
	[ $(($i % 3)) -eq 0 ] && echo "Waiting for background jobs to finish."
	i=$(($i+1))
	sleep 10
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task() {

    # hooks are called before a task is called
    # if a task is skipped, also its hooks are skipped
    # a hook can set the flag, so the accociated task is skipped

    local taskname=$1

    [ -f $LOGDIR/skip.$taskname ] || call_hook $taskname

    if [ -f $LOGDIR/skip.$taskname ]; then
        # skip task
	rm $LOGDIR/skip.$taskname
	[ "$verbose" ] && echo "Skiping task_$taskname"
	sndmon "TASKSKIP $taskname"
    else
	echo "Calling task_$taskname"
	sndmon "TASKBEGIN $taskname"
	terror=   # unset task error; task can set this variable to indicate an error
	task_$taskname
	sndmon "TASKEND $taskname $terror"
    fi
    # since the subroutine is not needed any more, we can undefine it
    unset task_$taskname
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
call_hook() {

    local hook=$1
    local cl dflag hfile
    [ "$debug" ] && dflag="-d"

    for cl in $classes; do
	hfile=$FAI/hooks/$hook.$cl
	if [ -x $hfile ]; then
	    echo "Calling hook: $hook.$cl"
	    # execute the hook
	    $hfile $dflag
	    check_status $hook.$cl $?
	fi
	if [ -x $hfile.source ]; then
	    echo "Source hook: $hook.$cl.source"
	    # source this hook
	    . $hfile.source $dflag
	    check_status $hook.$cl.source $?
	fi
     done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
skiptask() {

    # mark all given tasks, so they will be skipped
    local tasklist="$@"
    local task

    for task in $tasklist; do
	> $LOGDIR/skip.$task
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
get_fai_dir() {

    # get /fai directory; mount it or get it from a cvs repository
    if [ -z "$FAI_LOCATION" ]; then
       [ "$debug" ] && echo "Warning $0: \$FAI_LOCATION not defined."
       get_fai_cvs
    else
        mount $romountopt $FAI_LOCATION $FAI &&
	    echo "Configuration space $FAI mounted from $FAI_LOCATION"
    fi
    if [ ! -d $FAI/class ]; then
	die "$FAI/class not found. Aborting."
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
get_fai_cvs() {

    # subroutine which gets $FAI (/fai) configuration directory from
    # a cvs repository. You can redefine this subroutine if you need
    # access via ftp, http, or from a database

    if [ "$FAI_CVSROOT" ] ; then
        local TAG=""
	echo "Checking out CVS"
	[ -n "$FAI_CVSTAG" ] && TAG="-r $FAI_CVSTAG"
        export FAI_CONFIG_AREA=$FAI
        export FAI=/tmp/$(basename $FAI_CONFIG_AREA)
        
	[ "$debug" ] && echo "\$FAI now points to $FAI"
        
	if [ -d "$FAI_CONFIG_AREA" -a -z "$DONTCOPYCVS" ] ; then
	   echo "Config found at $FAI_CONFIG_AREA: Copying"
	   cp -a $FAI_CONFIG_AREA $FAI 
	   echo "Updating CVS"
	fi
	cd /tmp
	cvs -q -d"$FAI_CVSROOT" co -P -d $(basename "$FAI") \
	  $TAG $FAI_CVSMODULE > $LOGDIR/cvs.log
    else
	echo "Warning $0: Neither \$FAI_LOCATION nor \$FAI_CVSROOT are defined."
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
define_fai_flags() {

    local flag
    # FAI_FLAGS are comma separated, define all flags
    FAI_FLAGS=${FAI_FLAGS//,/ }
    for flag in $FAI_FLAGS; do
	# define this flag as 1
	eval "$flag=1"
	[ "$verbose" ] && echo "FAI_FLAGS: $flag=1"
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_setup() {

    # source user specific subroutines
    [ -f $FAI/hooks/subroutines ] && . $FAI/hooks/subroutines
    # set the system time and date using rdate or/and ntpdate
    [ "$TIMESRVS_1" ] && rdate $TIMESRVS_1
    [ "$NTPSRVS_1" ]  && ntpdate -b -v $NTPSRVS
    SECONDS=0 # set wall clock time to 0

    define_fai_flags
    DNSDOMAIN=$DOMAIN     # cfengine 1.5.3 can't use $DOMAIN

    [ "$createvt" ] && {
	# create two virtual terminals; acces via alt-F2 and alt-F3
	echo "Press ctrl-c to interrupt FAI and to get a shell"
	openvt -c2 /bin/bash ; openvt -c3 /bin/bash
	trap 'echo "You can reboot with faireboot";bash' INT QUIT
    }

    # start secure shell daemon for remote access
    [ "$sshd" -a -x /usr/sbin/sshd ] && /usr/sbin/sshd

    # when did FAI start, using localtime
    FAI_RUNDATE=$(date +'%Y%m%d_%H%M%S')

    cat >> $rcsfaivar <<-EOM
	FAI_RUNDATE=$FAI_RUNDATE
	LOGDIR=$LOGDIR
EOM
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_action() {

    if [ -z "$FAI_ACTION" ]; then
	echo "No action in \$FAI_ACTION defined."
	task_faiend
	exit 
    fi
    echo "FAI_ACTION: $FAI_ACTION"
    echo "FAI_ACTION=$FAI_ACTION" >> $rcsfaivar
    case $FAI_ACTION in

	install)
	    echo Performing FAI installation. All data may be overwritten!
	    echo -ne "\a"; sleep 1
	    echo -ne "\a"; sleep 1
	    echo  -e "\a"; sleep 5
	    task install
	    task faiend
	    ;;
	sysinfo)
	    echo Showing system information.
	    task sysinfo
	    task_faiend
	    die Now you have a shell.
	    ;;
	*)
	    if [ -f $FAI/hooks/$FAI_ACTION ]; then
		echo "Calling user defined action: $FAI_ACTION"
		$FAI/hooks/$FAI_ACTION
	    else
		echo "ERROR: User defined action $FAI/hooks/$FAI_ACTION not found."
		task_faiend
	    fi
	    ;;
	esac
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_defclass() {

    # new script for defining classes; variables imported: $LOGDIR, $verbose, $debug
    fai-class $FAI/class $LOGDIR/FAI_CLASSES
    classes=$(< $LOGDIR/FAI_CLASSES)

    # define classes as: a.b.c.d for cfengine -D
    # this doesn't work without echo
    cfclasses=`echo $classes`
    cfclasses=${cfclasses// /.}
    [ "$debug" ] && echo "cfclasses: $cfclasses"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_defvar() {

    cd $FAI/class
    for class in $classes ; do
	if [ -f $class.var ]; then
	    [ "$verbose" ] && echo "Executing $class.var"
	    [ "$debug" ] && set -vx
	    . $class.var </dev/null
	    [ "$debug" ] && set +vx
	fi
    done

    # /fai/class/S* scripts or hooks can write variable definitions
    # to additonal.var. now source these definitions
    [ "$debug" ] && set -vx
    [ -f $LOGDIR/additional.var ] && . $LOGDIR/additional.var
    [ "$debug" ] && set +vx
    unset class
    # now all variables are defined. Dump them to variables.sh
    set | perl -ne 'print if /^\w\w+=/' > $LOGDIR/variables.sh
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_mountdisks() {

    [ ! -f $LOGDIR/$fstab ] && die "No $LOGDIR/$fstab created."
    # mount swap space
    local sd
    for sd in $SWAPLIST; do
	swapon $sd && [ "$verbose" ] && echo "Enable swap device $sd"
    done
    mount2dir $FAI_ROOT $LOGDIR/$fstab
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_configure() {

    fai-do-scripts $FAI/scripts
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_savelog() {

    fai-savelog -l
    fai-savelog -r
    [ -d $target/etc/fai ] || mkdir -p $target/etc/fai
    cd $LOGDIR && cp -p FAI_CLASSES variables.sh $diskvar $target/etc/fai
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_faiend() {

    wait_for_jobs
    echo "Press <RETURN> to reboot or ctrl-c to execute a shell"
    # reboot without prompting if FAI_FLAG reboot is set
    [ -z $reboot ] && read
    echo "Rebooting $HOSTNAME now"
    sndmon REBOOT
    cd /
    sync

    case `uname -s` in
	Linux)
	    umount -ar
	    exec reboot -dfi
	    ;;
    esac
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_backup() {

    die "Task backup not yet used. But you can use the hook backup."
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_install() {

    > $stamp

    save_dmesg

    task partition
    task mountdisks
    task extrbase
    task mirror
    task updatebase
    task instsoft
    task configure
    task finish
    date
    echo "The installation took $SECONDS seconds."
    task chboot

    rm -f $stamp
    # save again, because new messages could be created
    save_dmesg
    task savelog

    if [ -f $stamp ]; then
	echo "Error while executing commands in subshell."
	echo "$stamp was not removed."
	die "Please look at the log files in $LOGDIR for errors."
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
