#!/bin/bash

#*********************************************************************
#
# fai -- main installation script executed after booting
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 1999-2017 by Thomas Lange, lange@informatik.uni-koeln.de
# Universitaet zu Koeln
# (c) 2001-2005 by Henning Glawe, glaweh@physik.fu-berlin.de
# Freie Universitaet Berlin
#
#*********************************************************************
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#*********************************************************************

#set -xv # for full debugging

export PATH=/usr/local/sbin:/usr/local/bin:/usr/lib/fai:/usr/sbin:/usr/bin:/sbin:/bin
# some variables
export FAI_VERSION=FAIVERSIONSTRING
stamp=/var/run/fai/FAI_INSTALLATION_IN_PROGRESS
export romountopt=${romountopt:-"-o async,noatime,nolock,ro,actimeo=1800"}

[ -n "$STOP_ON_ERROR" ] || export STOP_ON_ERROR=700
export faimond=0
export renewclass=0
export task_error=0 # tasks can set this variable to indicate an error

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fdie() {

    local e=$1   # first parameter is the exit code
    shift

    echo "$@" >&2 # print error message
    exit $e
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fai_init() {

    set -a # now export all variables
    set -o pipefail

    umask 022
    . /usr/lib/fai/subroutines

    mkdir -p /var/run/fai
    [ -f $FAI_ETC_DIR/fai.conf ] && . $FAI_ETC_DIR/fai.conf
    : ${FAI:=/var/lib/fai/config} # default value
    : ${MNTPOINT:=/media/mirror}  # default value
    : ${FAI_LOGPROTO:=ssh}        # default value
    [ -n "$cspace" ] && FAI_CONFIG_SRC=$cspace
    unset cspace

    # this test is not perfect. There may be false positives
    if [ -d /media/mirror ]; then   # we are booting from fai CD or USB stick
        romountopt=
        FAI_DEBMIRROR="--bind /media/mirror"
        MNTPOINT=/media/mirror
    fi

    [ -f "$stamp" ] && {
       echo -n "$0 already running or was aborted before. PID: " >&2
       cat $stamp >&2
       fdie 1 "You may remove $stamp and try again." >&2
    }

    DEBIAN_FRONTEND=noninteractive
    # local disks are mounted to $FAI_ROOT
    if [ -z "$FAI_ROOT" ] ; then
      [ $do_init_tasks -eq 1 ] && FAI_ROOT=/target || FAI_ROOT=/
    fi
    # executed command in the environment of the new system
    ROOTCMD="chroot $FAI_ROOT"
    # no chroot needed
    [ "$FAI_ROOT" = '/' ] && ROOTCMD=
    target=$FAI_ROOT
    AINSL_TARGET=$FAI_ROOT

    if [ $do_init_tasks -eq 1 ]; then
        trap 'echo "Now rebooting";faireboot' INT QUIT
    else
        trap "echo 'Aborted';rm -f $stamp" INT QUIT
    fi

    if [ $do_init_tasks -eq 1 ]; then
        eval_cmdline
        define_fai_flags
        # we really need to start udev
        if [ -x /etc/init.d/udev ]; then
	    /etc/init.d/udev start
        fi
        mkdir -p /var/run/sshd /var/run/network # when using initrd kernels
        ifup lo
        [ -x /sbin/portmap ] && /sbin/portmap
	if [ -x /sbin/rpcbind ]; then
	    pgrep rpcbind >/dev/null || /sbin/rpcbind
	fi
        pgrep rpc.statd > /dev/null || rpc.statd # idmapd needs this

        cat /proc/kmsg >/dev/tty4 &

        # fix IP address lifetime
        ip -4 addr show | \
            awk '/^[0-9]+: [^:]+:/ { iface = substr($2, 1, length($2) - 1); }
                 /^  *inet [0-9.]*\/[0-9]* / && iface != "lo" { print $2, iface; }' | \
        while read addr iface; do
            ip -4 addr change "$addr" dev "$iface" valid_lft forever preferred_lft forever
        done

        # tune some nfs parameters
        mount -oremount,ro,noatime /live/image

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

    # since HOSTNAME may change define classes now, so we can call hooks before fai-class is called
    [ -z "$classes" ] && classes="DEFAULT $(uname -s | tr a-z A-Z) $HOSTNAME LAST"

    prcopyleft
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
usage() {
    cat <<-EOF
        fai $FAI_VERSION. Copyright (C) 1999-2016 Thomas Lange
        Usage: $0 [options] [action]

        Options:
           -v|--verbose         display more information during the update
           -h|--help            display this help message
           -N|--new             renew list of classes
           -c|--class           comma separated list of classes
           -C|--cfdir CFDIR     Use CFDIR for  reading the config files
           -s|--cspace CSDIR    URI of the configuration space
           -u|--hostname HNAME  set hostname to be used

EOF
    exit 0
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fstart() {

    # these tasks can define variables, that are needed later
    [ -n "$etc_message" ] && echo ""
    echo "$etc_message"
    [ $do_init_tasks -eq 1 ] || echo "Using configuration files from $FAI_ETC_DIR"
    unset etc_message
    task confdir
    # if the config space is a local directory, reset $FAI
    local method=$(expr match "$FAI_CONFIG_SRC" '\([^+]*\).*://')
    if [ $method = "file" ]; then
        local localpath=$(expr match "$FAI_CONFIG_SRC" '.*://\(/.*\)')
        export FAI=$localpath
    fi
    task setup
    task defclass
    unset renewclass
    [ $do_init_tasks -eq 1 ] && set_disk_info
    task defvar
    [ $do_init_tasks -eq 1 ] && load_keymap_consolechars
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Main routine

# Parse commandline options
TEMP=$(getopt -o s:u:Nhvc:C: --long cspace:,hostname:,new,help,verbose,class:,cfdir: -n "$0" -- "$@")
if [ $? != 0 ] ; then fdie 6 "Wrong option. Terminating." >&2 ; fi
# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"
unset TEMP

while true ; do
    case "$1" in
        -h|--help)
            usage ;;
        -v|--verbose)
            export verbose=1
            shift ;;
        -N|--new)
            renewclass=1
            shift ;;
        -C|--cfdir)
            cfdir=$2
            shift 2 ;;
        -c|--class)
            export cmdlineclasses=$2
            cmdlineclasses=${cmdlineclasses//,/ }
            export classes=$cmdlineclasses
            shift 2 ;;
        -s|--cspace)
            cspace=$2
            shift 2 ;;
        -u|--hostname)
            export newhostname=$2
            shift 2 ;;
        --)
            shift
            break ;;
         *)
            fdie 1 "$0: command line parsing error ! $@" >&2 ;;
    esac
done

# use FAI_ETC_DIR from environment variable
if [ -n "$FAI_ETC_DIR" -a -z "$cfdir" ]; then
    # print this message later so it gets into the log files
    etc_message="Using environment variable \$FAI_ETC_DIR."
fi
[ -n "$cfdir" ] && FAI_ETC_DIR=$cfdir
unset cfdir
: ${FAI_ETC_DIR:=/etc/fai}
FAI_ETC_DIR=$(readlink -f $FAI_ETC_DIR) # canonicalize path
export FAI_ETC_DIR

# override FAI_ACTION later if a command line argument is given
[ "$1" ] && export FAI_ACTION=$1
[ "$2" ] && export FAI_ROOT=$2 # only used for dirinstall

if [ $(id -u) != "0" ]; then
    fdie 1 "Run this program as root." >&2
fi

export FAI_RUNDIR=$PWD
export start_seconds=$(cut -d . -f 1 /proc/uptime)

if [ X$FAI_ACTION = Xdirinstall ]; then
    if [ -z "$FAI_ROOT" ]; then
        fdie 3 "Please specify a target directory. Aborted" >&2
    fi
    if [ $renewclass -eq 0 -a -z "$cmdlineclasses" ]; then
        fdie 4 "Please use -c or -N. Aborted" >&2
    fi

    # two lines taken from task_dirinstall
    mkdir -p $FAI_ROOT
    FAI_ROOT=$(cd $FAI_ROOT;pwd)

    # check if target directory is mounted with bad options
    fs=$(df -P $FAI_ROOT | tail -1 | awk '{print $6}')
    if mount | grep "on $fs " |  awk '{print $6}' | egrep -q "nosuid|nodev"; then
        fdie 5 "Target directory is mounted using nosuid or nodev. Aborting" >&2
    fi
    unset fs

    if [ ! -e $FAI_ETC_DIR/nfsroot.conf ]; then
        echo "$FAI_ETC_DIR/nfsroot.conf not found." >&2
        fdie 7 "You may want to install the package fai-server" >&2
    fi
    export NFSROOT=$(source $FAI_ETC_DIR/nfsroot.conf; echo $NFSROOT)
    export FAI_DEBOOTSTRAP=$(source $FAI_ETC_DIR/nfsroot.conf; echo $FAI_DEBOOTSTRAP)
    export FAI_DEBOOTSTRAP_OPTS=$(source $FAI_ETC_DIR/nfsroot.conf; echo $FAI_DEBOOTSTRAP_OPTS)

fi

# exit if we do not run from nfsroot and no parameter is given
if [ ! -f /.THIS_IS_THE_FAI_NFSROOT -a "X$1" = "X" ]; then
    fdie 2 "Please give more parameters if not run from the nfsroot." >&2
fi

# are we called as an init substitute ?
export do_init_tasks=0
[ "$0" = "/etc/init.d/rcS" ] && do_init_tasks=1
if [ $do_init_tasks -eq 1 ]; then

    # if hostname was set on the kernel command line (mostly when booting from CD)
    for word in $(< /proc/cmdline) ; do
        case $word in
            hostname=*)
            HOSTNAME=${word#*hostname=}
            ;;
        esac
    done
    unset word
    if [ -n "$HOSTNAME" ]; then
	echo $HOSTNAME > /proc/sys/kernel/hostname
	export HOSTNAME
    fi

    # if the host name is still not set, use the IP for setting the hostname
    read hname < /proc/sys/kernel/hostname
    if [ -z "$HOSTNAME" -o $HOSTNAME = '(none)' -o -z "$hname" ]; then
	myip=$(ip ad show up | grep -P -o '(?<=inet )[^/]+' | grep -v 127.0.0.1| tr '.' '-')
	if [ -n "$myip" ]; then
	    export HOSTNAME="ip-$myip"
	else
	    export HOSTNAME=debian
	fi
	echo $HOSTNAME > /proc/sys/kernel/hostname
	echo "Setting the host name: $HOSTNAME"
    fi
    unset hname myip

    renewclass=1 # always renew class list when installing
fi

[ -n "$newhostname" ] && export HOSTNAME=$newhostname

if [ $do_init_tasks -eq 1 ]; then
    # we are running an initial installation
    export LOGDIR=/tmp/fai
    mkdir -p $LOGDIR
else
    # this is for dirinstall, softupdate, ..
    export fai_rundate=$(date +'%Y%m%d_%H%M%S')
    export LOGDIR=/var/log/fai/$HOSTNAME/$FAI_ACTION-$fai_rundate
    if [ -d $LOGDIR ]; then
	fdie 13 "$LOGDIR already exists. MAybe another process is running. Aborting"
    fi
    mkdir -p $LOGDIR
    ln -snf $FAI_ACTION-$fai_rundate $LOGDIR/../last-$FAI_ACTION
    ln -snf $FAI_ACTION-$fai_rundate $LOGDIR/../last
fi
chown root:adm $LOGDIR
chmod 0750 $LOGDIR

fai_init

if [ $do_init_tasks -ne 1 ]; then
    echo "Starting FAI execution - $fai_rundate" | tee -a $LOGDIR/fai.log
fi
[ -n "$newhostname" ] && echo "Hostname set to $HOSTNAME" | tee -a $LOGDIR/fai.log
unset newhostname

fstart > >( tee -a $LOGDIR/fai.log )  2>&1

task action |& tee -a $LOGDIR/fai.log
if [ ${PIPESTATUS[0]} -gt 0 ]; then
    final_exit_code=1
fi
if [ -f $LOGDIR/task_error ]; then
    task_error=$(< $LOGDIR/task_error)
    if [ $task_error -gt 0 ]; then
	final_exit_code=1
    fi
fi


[ -L "/var/run/fai/current_config" ] && rm -f "/var/run/fai/current_config"

exit $final_exit_code
