#! /bin/bash

#*********************************************************************
#
# subroutines -- useful subroutines for FAI
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2000-2025 by Thomas Lange, lange@cs.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
#*********************************************************************

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

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

    # echo comment and exit installation
    [ -f $LOGDIR/skip.savelog ] || call_hook savelog
    [ -f $LOGDIR/skip.savelog ] || task_savelog
    echo "$*"
    if inside_nfsroot; then
        exec bash -i
    else
        /usr/lib/fai/fai-abort
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
defnop() {

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

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

    # if we have a partition MY-DATA mount it
    if [ -e /dev/disk/by-label/MY-DATA ]; then
        mkdir -p /media/data
        mount /dev/disk/by-label/MY-DATA /media/data
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
install_pkgs() {

    # install all packages found in the subdirecories pkgs/<CLASSNAME>

    local c pkgs dirs pdir ttdir
    local debdir=$1

    if [ ! -d $debdir ]; then
        return
    fi

    pdir=/tmp/pkgs
    ttdir=${target}$pdir
    mkdir $ttdir
    mount --bind $debdir $ttdir

    for c in $classes; do
        if [ -d $ttdir/$c ]; then
            dirs+=" $c"
        fi
    done

    if [ -z "$dirs" ]; then
        umount $ttdir
        return
    fi

    pkgs=$(cd $ttdir;find $dirs -name \*.deb)
    if [ -n "$pkgs" ]; then
        echo "Installing .deb files from $debdir"
        $ROOTCMD bash -c "cd $pdir; dpkg -R -i $dirs"
    fi

    pkgs=$(cd $ttdir;find $dirs -name \*.rpm)
    if [ -n "$pkgs" ]; then
        echo "Installing .rpm files from $debdir"
        $ROOTCMD bash -c "cd $pdir; rpm -i $pkgs"
    fi
    umount $ttdir
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rwmount() {

    # remount partition read/write, for interactive use only
    mount -o rw,remount $1
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
rw_target() {

    # make $target and mounted subdirectories read-write
    local dir
    for dir in $(mount | grep "on $target" | grep -E -v "media/mirror|tmpfs"| awk '{print $3}'); do
        rwmount $dir
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ctam() {

    # chroot /target mount
    # mount pseudo filesystems into /target
    if [ -z "$target" -o "$target" = "/" ]; then
        echo "ERROR: \$target is not set (correctly)."
        return
    fi
    if [ -f $target/proc/cpuinfo ] ; then
        echo "Filesystems already mounted to $target."
        return
    fi

    mount -t proc   proc   $target/proc
    mount -t sysfs  sysfs  $target/sys
    mount --bind    /dev   $target/dev
    mount --make-private   $target/dev
    mount -t devpts devpts $target/dev/pts
    mount --make-private   $target/dev/pts

    if [ "$FAI_ACTION" = "softupdate" -o "$FAI_ACTION" = "dirinstall" ]; then
        :  # do not mount efivars for these actions
    else
        if [ -d /sys/firmware/efi ]; then
            mount --bind /sys/firmware/efi/efivars $target/sys/firmware/efi/efivars
        fi
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ctau() {

    # chroot /target umount
    # umount pseudo filesystems from /target
    if [ -z "$target" -o "$target" = "/" ]; then
        echo "ERROR: \$target is not set (correctly)."
        return
    fi
    if [ ! -f $target/proc/cpuinfo ] ; then
        echo "Filesystems already umounted from $target."
        return
    fi

    for f in /dev/pts /dev /proc /sys/firmware/efi/efivars /sys; do
        mountpoint -q ${target}$f && umount ${target}$f
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ctai() {

    # chroot into target
    if [ -z "$target" -o "$target" = "/" ]; then
        echo "ERROR: \$target is not set (correctly)."
        return
    fi
    if [ ! -f $target/bin/bash ]; then
        echo "ERROR: Cannot chroot into $target."
        return
    fi
    chroot $target bash
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
save_dmesg() {

    inside_nfsroot || return
    if [ -x /usr/sbin/logtail ] ; then
      logtail -f /var/log/kern.log -o /var/lib/fai/kern.log.offset >> $LOGDIR/dmesg.log
    else
      dmesg > $LOGDIR/dmesg.log
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
umount_csspace() {

    # umount config space if accessed via nfs
    if [[ $FAI_CONFIG_SRC =~ ^nfs:// ]]; then
        mountpoint -q $FAI && umount $FAI
    fi
    if [[ $FAI_CONFIG_SRC =~ ^https?:// ]]; then
        umount /var/lib/fai
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
umount_target() {

    if [ "$target" != '/' ]; then
	# do not umount during softupdate
	umount $target/proc $target/sys/firmware/efi/efivars $target/sys $target/dev/pts $target/dev 2>/dev/null
	for dir in $(mount | grep $target | grep -E -v "media/mirror"| awk '{print $3}' | sort -r); do
            if [ "$dir" = "$target" ]; then
               # do not umount an empty target directory
               [ -z "$(ls -A "$target")" ] && return
            fi
            mountpoint -q $dir && umount $dir
	done
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cleanup_devicemapper() {

    local vgroup v
    # remove device mapper devices
    grep -q 'Executing: vgcreate' $LOGDIR/format.log 2>/dev/null
    if [ $? -eq 0 ]; then

	# write which vg should be removed, PPID is the fai-diskimage process
	> /var/run/fai/vgremove.$PPID
	vgroup=$(awk '/Executing: vgcreate/ {print $3}' $LOGDIR/format.log)
	for v in $vgroup; do
	    echo blkdeactivate /dev/$v/* >> /var/run/fai/vgremove.$PPID
	done
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
mk_zerofree_list() {

    # make a list of devices for zerofree which have an ext2/3/4 file  system
    local source fstype mpoint v

    if [ X$verbose = X1 ]; then
	v=-v
    fi
    while read -r source fstype mpoint ; do

	# skip non ext2/3/4 file systems
	case $fstype in
            ext*) : ;;
	    *) continue ;;
	esac

	if [[ $mpoint =~ $target ]]; then
	    echo "zerofree $v $source" >> /var/run/fai/zerofree.$PPID
	fi

    done < <(df --output=source,fstype,target)
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
stop_fai_installation() {

    # this subroutine should directly stop the installation process
    sendmon "TASKEND $taskname $task_error"
    echo "Error in task $taskname. Code: $task_error" >&2
    echo "Traceback: $task_error_func" >&2
    die "FATAL ERROR. Installation stopped."
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:      $task_error
# Required-Var:      $1 $2 $task_error
# Short-Description: save the maximum error code,
# Short-Description: $1 is the error that will be saved unless $2 is zero
# Short-Description: if $3 is set to 1 only $task_error is set but the
# Short-Description: installation is not stopped
### END SUBROUTINE INFO

task_error() {

    local current
    [ X$2 = X0 ] && return
    task_error_func=${FUNCNAME[*]}

    # save max error for current task
    task_local_error=$1
    current=$(< $LOGDIR/task_local_error)
    if [ $task_local_error -gt $current ]; then
	echo $task_local_error > $LOGDIR/task_local_error
    fi

    # save global max task error
    task_error=$(< $LOGDIR/task_error)
    if [ $task_local_error -gt $task_error ]; then
     task_error=$task_local_error
     echo $task_local_error > $LOGDIR/task_error
    fi
    if [ X$3 = X1 ]; then
	:
    else
	[ $task_error -gt $STOP_ON_ERROR ] && stop_fai_installation
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:      $task_error
# Required-Var:      $LOGDIR
# Short-Description: call a certain task
### END SUBROUTINE INFO

declare -A taskduration
export duration=0
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
    shift
    local task_local_error=0 # the error code set by every task

    echo 0 > $LOGDIR/task_local_error
    [ -f $LOGDIR/skip.$taskname ] || call_hook $taskname "$@"

    if [ -f $LOGDIR/skip.$taskname ]; then
        # skip task
        rm $LOGDIR/skip.$taskname # TODO: remove skip files at the very end
        [ X$verbose = X1 ] && echo "Skipping task_$taskname"
        sendmon "TASKSKIP $taskname"
    else
        echo "Calling task_$taskname"
        sendmon "TASKBEGIN $taskname"
        task_error_func=''
        taskduration[$taskname]=$SECONDS
        task_$taskname "$@"
        [ $duration -eq 1 ] && echo "Duration of task $taskname: " $((SECONDS-${taskduration[$taskname]}))"s"
	task_local_error=$(< $LOGDIR/task_local_error)
        sendmon "TASKEND $taskname $task_local_error"
        if [ "$task_local_error" -ne 0 ] ; then
          echo "Exit code task_$taskname: $task_local_error" >&2
        fi
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:
# Required-Var:      $classes $debug
# Short-Description: call a hook, hook.sh can define additional variables
### END SUBROUTINE INFO

call_hook() {

    local hook=$1
    shift

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

    for cl in $classes; do
        hfile=$FAI/hooks/$hook.$cl
        if [ -f $hfile -a ! -x $hfile ]; then
            echo "WARNING: Skipping $hfile because it's not executable." >&2
            continue
        fi
        if [ -f $hfile.sh -a ! -x $hfile.sh ]; then
            echo "WARNING: Skipping $hfile.sh because it's not executable." >&2
            continue
        fi
        if [ -f $hfile.source ]; then
            echo "ERROR: The suffix .source is deprecated. Use .sh instead." >&2
            # continue
        fi
        if [ -x $hfile.sh ]; then
            echo "Source hook: $hook.$cl.sh"
            sendmon "HOOK $hook.$cl.sh"
            # source this hook
            . $hfile.sh $dflag "$@"
            check_status $hook.$cl.sh $?
        fi
        if [ -x $hfile ]; then
            echo "Calling hook: $hook.$cl"
            sendmon "HOOK $hook.$cl"
            # execute the hook
            $hfile $dflag "$@"
            check_status $hook.$cl $?
        fi
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
skiptask() {

    # mark all given tasks, so they will be skipped
    local task

    for task in "$@"; do
        echo > $LOGDIR/skip.$task # create file with size != 0
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
define_fai_flags() {

    local flag
    # FAI_FLAGS are comma separated, define all flags
    FAI_FLAGS=${FAI_FLAGS//,/ }
    echo "FAI_FLAGS: $FAI_FLAGS"
    for flag in $FAI_FLAGS; do
        # define this flag as 1
        eval "flag_$flag=1"
    done
    [ "$flag_verbose" ] && verbose=1 # for backward compability
    [ "$flag_debug" ]   && debug=1   # for backward compability
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    $fai_rundate
# Requires-Var:
# Suggests-Var:    $flag_createvt $flag_sshd
# Short-Description: <task desc.>
### END SUBROUTINE INFO

task_setup() {

    # source user specific subroutines
    [ -f $FAI/hooks/subroutines ] && . $FAI/hooks/subroutines

    define_fai_flags

    # this may be moved to an external script
    if inside_nfsroot; then
        # set the system time and date using rdate or/and ntpdate
        [ "$TIMESRVS_1" ] && [ -f /bin/rdate ] && rdate $TIMESRVS_1
        [ -n "$NTPSRVS" ]  && ntpdate -b $NTPSRVS
        # Save the updated time to hardware, so when udevadm trigger
        # triggers a re-read of the time from rtc, it isn't reset
        hwclock --systohc
        [ "$flag_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
            echo "The log files are saved in /tmp/fai" > /dev/tty2
            echo "The log files are saved in /tmp/fai" > /dev/tty3
            trap 'echo "You can reboot with faireboot"; \
	          echo "The log files are saved in /tmp/fai"; \
		  cd /tmp/fai;bash' INT QUIT
        }

        systemctl start dbus
        # start secure shell daemon for remote access
        if [ "$flag_sshd" -a -x /usr/sbin/sshd ]; then
            pgrep -x sshd >/dev/null || /usr/sbin/sshd
        fi

        udevadm trigger
    fi
    unset flag_createvt flag_sshd

    # now you have enough time to make changes to the config space
    # only for debugging
    if [ -n "$flag_wait" ]; then
        echo "Sleeping. Now you may change the config space in $FAI."
        echo "Continue after killall sleep."
        sleep 50000
    fi

    # when did FAI start, using localtime
    : ${fai_rundate:=$(date +'%Y%m%d_%H%M%S')}
    if inside_nfsroot; then
        echo "Starting FAI execution - $fai_rundate"
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    none
# Requires-Var:    $FAI_ACTION
# Short-Description: call task depending on $FAI_ACTION
### END SUBROUTINE INFO

task_action() {

    if [ -z "$FAI_ACTION" ]; then
        echo "No action in \$FAI_ACTION defined."
        sendmon "TASKERROR action 21"
        task_faiend
        exit
    fi

    echo "FAI_ACTION: $FAI_ACTION"
    case $FAI_ACTION in
        install)
            if [ $target = "/" ]; then
                echo "Cowardly refusing to run a FAI installation on a running system."
                return
            fi
            echo Performing FAI installation. All data may be overwritten!
            if inside_nfsroot && [ X$flag_menu = X ] ; then
		echo -ne "\a"; sleep 1
		echo -ne "\a"; sleep 1
		echo  -e "\a"; sleep 5
	    fi
            task install
            task faiend
            ;;
        dirinstall)
            task dirinstall
            task faiend
            ;;
        softupdate)
            task softupdate
            task faiend
            ;;
        sysinfo)
            task sysinfo
            task_faiend
            die Now you have a shell.
            ;;
        inventory)
            task inventory
            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
		task_faiend
            else
                echo "ERROR: User defined action $FAI/hooks/$FAI_ACTION not found." >&2
                sendmon "TASKERROR action 22"
                task_faiend
            fi
            ;;
    esac
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    $classes $cfclasses
# Requires-Var:    $LOGDIR
# Suggests-Var:    $renewclass
# Short-Description: define FAI classes and store them in several shell variables
### END SUBROUTINE INFO

task_defclass() {

    if [ ! -d $FAI/class ]; then
        sendmon "TASKERROR defclass 21"
        echo "Subdirectory $FAI/class missing in config space. Following subdirectories are found:"
        find $FAI -maxdepth 1 -type d -printf "%p\n"
        die "Aborting."
    fi

    # new script for defining classes; variables imported: $LOGDIR, $verbose, $debug
    if [ $renewclass -eq 1 ]; then
        # reevaluate new list of classes
        fai-class -T $FAI/class $LOGDIR/FAI_CLASSES
	# check task error again
	local lerr=$(< $LOGDIR/task_local_error)
	task_error $lerr

        classes=$(< $LOGDIR/FAI_CLASSES)
    elif [ -n "$cmdlineclasses" ]; then
        classes=$cmdlineclasses
    elif [ ! -f /var/lib/fai/FAI_CLASSES ]; then
        # use classes defined at installation time
        die "Try to read classes from /var/lib/fai/FAI_CLASSES. Failed. Aborting."
    else
        classes=$(< /var/lib/fai/FAI_CLASSES)
    fi
    echo "List of all classes: " ${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"
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_defvar() {

    local showvar=1 # TODO: new FAI_FLAG or always set when verbose is used
    [ "$showvar" ] && set -x
    . $1 </dev/null
    [ "$showvar" ] && set +x
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_defvar() {

    local svar=$LOGDIR/showvar.log
    local odir=$(pwd)
    cd $FAI/class
    for class in ${classes:-} ; do
        if [ -f $class.var -a -r $class.var ]; then
            [ X$verbose = X1 ] && echo "Executing $class.var"
            # show only lines with ++, we cannot use a pipe, since it would call
            # _devfar in a subprocess. Then, variables are not defined
            _defvar $class.var > $svar 2>&1
            grep -P '^\+\+ \w+=' $svar| sed -e 's/\(.*PW\)=.*$/\1=XXXXXXXXXXXXX/'
            rm $svar
        fi
    done

    # /fai/class/* scripts or hooks can write variable definitions
    # to additonal.var. now source these definitions
    if [ -f $LOGDIR/additional.var -a -r $LOGDIR/additional.var ]; then
        echo "Defining variables from additional.var"
        _defvar $LOGDIR/additional.var > $svar 2>&1
        grep ^++ $svar
        rm $svar
    fi
    cd $odir
    unset class svar odir
    # now all variables are defined. Dump them to variables.log, so we can sources them if needed
    set | perl -ne 'print if /^\w\w+=/ and not /^(EUID|PPID|SHELLOPTS|UID|rootpw|ROOTPW|USERPW|HOME|PWD|BASHOPTS)|\(/' > $LOGDIR/variables.log
    # another approach is to use this. A slightly different format, but seems to be robust.
    # declare -x > $LOGDIR/variables.log
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_mountdisks() {

    [ ! -f $LOGDIR/fstab ] && die "No $LOGDIR/fstab created."
    # mount swap space
    if inside_nfsroot; then
	local sd
	for sd in $SWAPLIST; do
            swapon -p1 $sd && [ X$verbose = X1 ] && echo "Enable swap device $sd"
	done
    fi
    mount2dir $FAI_ROOT $LOGDIR/fstab
    if [ "$?" -ne 0 ]; then
        sendmon "TASKERROR mountdisks 885"
        task_error 885
    fi
    if inside_nfsroot && ! mountpoint -q $target; then
        echo "$target is not a mount point. Check your disk_config file."
        sendmon "TASKERROR mountdisks 886"
        task_error 886
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_configure() {

    fai-do-scripts $FAI/scripts
    task_error 420 $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_tests() {

    if [ ! -d $FAI/tests ]; then
	return
    fi
    fai-do-scripts $FAI/tests  # always returns 0 atm
    # check if any test failed
    if [ -f $LOGDIR/test.log ]; then
        if grep -q "FAILED with " $LOGDIR/test.log; then
            sendmon "TASKERROR tests 312"
            task_error 312
            return 1
        fi
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_savelog() {

    if [ -d /media/data/ ]; then
        mkdir -p /media/data/logs
        fai-savelog -l /media/data/logs
    fi
    if [ -w $FAI_ROOT/var/log/fai ]; then
        mkdir -p  $FAI_ROOT/var/{lib,log}/fai
        fai-savelog -l
        [ -f $LOGDIR/FAI_CLASSES ] && cp -pu $LOGDIR/FAI_CLASSES $FAI_ROOT/var/lib/fai
        [ -f $LOGDIR/disk_var.sh ] && cp -pu $LOGDIR/disk_var.sh $FAI_ROOT/var/lib/fai
    fi
    fai-savelog -r
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Description: Always called as last FAI task
### END SUBROUTINE INFO
task_faiend() {

    : ${flag_reboot:=0}
    : ${flag_halt:=0}

    # reboot/halt without prompting if FAI_FLAG reboot or halt is set
    # wait for keypress if neither flag reboot nor halt is set
    if inside_nfsroot && [ "$flag_reboot" -eq 0 ] && [ "$flag_halt" -eq 0 ]; then
        echo "Press <RETURN> to reboot (do not remove your removable medium yet)."
        read
    fi

    sendmon "TASKEND faiend 0"

    cd /
    mk_zerofree_list
    umount_target
    cleanup_devicemapper
    if ! inside_nfsroot; then
	echo "Log files are saved in $LOGDIR"
	return 0
    fi
    killall -q sshd systemd-udevd
    umount $FAI_ROOT/proc $FAI_ROOT/sys/firmware/efi/efivars $FAI_ROOT/sys $FAI_ROOT/dev/pts 2>/dev/null
    umount -arf 2>/dev/null

    echo "Rebooting $HOSTNAME now"
    sendmon "TASKEND reboot 0"
    # reboot or halt?
    if [ "$flag_halt" -gt "0" ]; then
        exec halt -dfp
    else
        exec reboot -df
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_install() {

    echo $$ > $stamp

    save_dmesg

    task partition
    task mountdisks
    task extrbase
    task debconf
    task repository
    task updatebase
    task instsoft
    task configure
    task tests
    task finish
    task chboot

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

    if [ -f $stamp ]; then
        echo "Error while executing commands in subshell." >&2
        echo -n "$stamp was not removed. PID of running process: "
        cat $stamp
        sendmon "TASKERROR install 21"
        die "Please look at the log files in $LOGDIR for errors." >&2
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_dirinstall() {

    mkdir -p $FAI_ROOT
    FAI_ROOT=$(cd $FAI_ROOT;pwd)
    stamp=/var/run/fai/dirinstall-${FAI_ROOT//\//=}
    clean_exit() {
        rm -f $stamp
        [ -z "$FAI_ROOT" ] && return
        # sometimes mountpoint may be mounted twice, so try to umount them twice
        mountpoint -q $FAI_ROOT/proc && umount $FAI_ROOT/proc
        mountpoint -q $FAI_ROOT/sys  && umount $FAI_ROOT/sys
        mountpoint -q $FAI_ROOT/proc && umount $FAI_ROOT/proc
        mountpoint -q $FAI_ROOT/sys  && umount $FAI_ROOT/sys
        mountpoint -q $FAI_ROOT/dev/pts && umount $FAI_ROOT/dev/pts
        mountpoint -q $FAI_ROOT/run/udev && umount $FAI_ROOT/run/udev
        # sometimes umount $FAI_ROOT/dev fails, because a process is
        # still running in the background and accesses /dev
        # this occured sometimes when using dirinst and a long package
        # list if dhelp.postinst is starting an index process in the
        # bg which did not finished until the installation was finished.
        mountpoint -q $FAI_ROOT/dev && umount $FAI_ROOT/dev
        mkramdisk -au 2>/dev/null
    }
    trap 'clean_exit' INT QUIT EXIT



    [ -f "$stamp" ] && {
       echo -n "fai dirinstall into directory $FAI_ROOT already running or was aborted before. PID: "
       cat $stamp
       echo "You may remove $stamp and try again."
       exit 1
    }

    echo $$ > $stamp
    echo "Installing into directory $FAI_ROOT"
    task extrbase
    [ -f $target/etc/fstab ] || touch $target/etc/fstab
    task debconf
    task repository
    task updatebase
    task instsoft
    task configure
    task tests
    task finish
    clean_exit

    echo -n "Size of file system created:  "; du -sh $FAI_ROOT
    rm -f $stamp
    unset LOGUSER # so logfile are not saved to remote
    task savelog

    if [ -f $stamp ]; then
        echo "Error while executing commands in subshell." >&2
        echo -n "$stamp was not removed. PID of running process: "
        cat $stamp
        sendmon "TASKERROR install 21"
        die "Please look at the log files in $LOGDIR for errors." >&2
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_softupdate() {

    echo Performing FAI system update. All data may be overwritten!
    stamp=/var/run/fai/fai_softupdate_is_running
    [ -f "$stamp" ] && die "Lock file $stamp found. Another fai softupdate is already running. Aborting."
    echo $$ > $stamp
    trap "rm -f $stamp" INT QUIT EXIT

    # the following copy operation is required to make $LOGDIR a reliable source
    # for disk_var.sh
    # use the last disk_var during update if available
    [ -f /var/lib/fai/disk_var.sh ] && cp -p /var/lib/fai/disk_var.sh $LOGDIR

    save_dmesg

    task debconf
    task repository
    task updatebase
    task instsoft
    task configure
    task tests
    task finish

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

    if [ -f $stamp ]; then
        echo "Error while executing commands in subshell." >&2
        echo -n "$stamp was not removed. PID of running process: "
        cat $stamp
        sendmon "TASKERROR softupdate 21"
        die "Please look at the log files in $LOGDIR for errors." >&2
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

catnc() {
    # cat but no comment lines
    grep -E -v "^#" $@
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    $disklist
# Requires-Var:
# Short-Description: create list of available disks
### END SUBROUTINE INFO

set_disk_info() {

    # the variable holds a space separated list of devices
    disklist=$(fai-disk-info | sort | tr '\n' ' ')
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
eval_cmdline() {

    # parse kernel parameters and define variables
    local word cmdline

    echo "Kernel currently running: "
    uname -rsmo
    cmdline="$(</proc/cmdline)"
    echo "Kernel parameters: $cmdline"
    for word in $cmdline; do
	if echo "$word" | grep -E -q '^[a-zA-Z0-9_]+=' ; then
            eval "export $word"
	fi
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
read_fai_monitor_vars() {

    # read variables which are received from the fai-monitor
    if [ ! -s /run/initramfs/fai-variables ]; then
	return
    fi

    eval $(grep FAI_CONFIG_SRC /run/initramfs/fai-variables)
    eval $(grep FAI_FLAGS /run/initramfs/fai-variables)
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    $faimond $sendid $SERVER $monserver
# Requires-Var:    $LOGDIR $FAI
# Suggests-Var:    $monserver $FAI_SENDID $FAI_MONITOR_PORT
# Short-Description: <task desc.>
### END SUBROUTINE INFO

task_confdir() {

    if inside_nfsroot; then

	read_fai_monitor_vars
        eval_cmdline

        get-boot-info
        echo "Reading $LOGDIR/boot.log"
        . $LOGDIR/boot.log
        unset T170 T171 T172 ROOT_PATH BOOTFILE

        printk=${printk:-6}
        echo $printk > /proc/sys/kernel/printk
        create_resolv_conf
    fi
    define_fai_flags

    if [ -z "$SERVER" ]; then
        # since SERVER is not set (by DHCP) we extract it from FAI_CONFIG_SRC
        # extract server name from FAI_CONFIG_SRC and delete user@ from string
        SERVER=$(expr match "$FAI_CONFIG_SRC" '.*://\([^/]*\)/.*' | sed -e 's/.*@//')
        if [ -n "$SERVER" ]; then
          echo "Setting SERVER=$SERVER. Value extracted from FAI_CONFIG_SRC."
        else
          SERVER=$(awk '{if($2 == "/run/rootfsbase") print $1;}' /proc/mounts | grep :/ | cut -d ':' -f 1)
          if [ -n "$SERVER" ]; then
            echo "Setting SERVER=$SERVER. Value extracted from NFSROOT."
          elif [ "$FAI_CONFIG_SRC" = "detect://" ]; then
              :
          else
            echo "NO SERVER FOUND FOR FAI_CONFIG_SRC"
          fi
        fi
    fi

    # check if monitor server is available
    : ${monserver:=$SERVER}
    : ${FAI_MONITOR_PORT:=4711}

    if [ -z "$monserver" ]; then
        echo "No monitor daemon defined."
        faimond=0
    else
        faimond=1
	if [ -n "$NIC1" ]; then
	    MAC=$(< /sys/class/net/$NIC1/address)
            PXE="01-"${MAC//:/-}
	fi

        : ${FAI_SENDID:=host} # set default if undefined
        case "$FAI_SENDID" in
            host) sendid=$HOSTNAME ;;
            mac)  sendid=$MAC ;;
            pxe)  sendid=$PXE ;;
            *)    sendid=$FAI_SENDID ;;
        esac

        if sendmon check; then
            echo "Monitoring to server $monserver enabled."
            sendmon "TASKBEGIN confdir"
        else
            faimond=0
            echo "Can't connect to monserver on $monserver port $FAI_MONITOR_PORT. Monitoring disabled."
        fi
    fi

    local mdir=/lib/modules/$(uname -r)
    if inside_nfsroot && [ ! -d $mdir ]; then
        echo "ERROR: The running kernel does not match the kernel modules inside the nfsroot."
        echo "ERROR: Kernel modules directory $mdir not available. Only found:"
        ls -d /lib/modules/*
        echo ""
        task_error 790
    fi

    get-config-dir || {
        echo "Problems accessing the config space."
        die ""
    }
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    $BOOT_DEVICE $ROOT_PARTITION $BOOT_PARTITION $SWAPLIST
# Requires-Var:    $LOGDIR $LOGDIR/disk_var.sh
# Short-Description: partition local hard disks
### END SUBROUTINE INFO

task_partition() {

    [ ! -s $LOGDIR/disk_var.sh ] && setup-storage -X |& tee $LOGDIR/format.log

    # if we've created device mapper devices, do cleanup on error
    grep -q 'Executing: vgcreate' $LOGDIR/format.log 2>/dev/null
    if [ $? -eq 0 ]; then
	trap "umount_target;cleanup_devicemapper" QUIT EXIT
    fi

    # partitioning tool must create $LOGDIR/disk_var.sh file
    if [ ! -s $LOGDIR/disk_var.sh ]; then
        local conffile=$(grep "Using config file:" $LOGDIR/format.log | cut -d: -f2 )
        if [ -n "$conffile" ]; then
            echo ""
            echo "This is your disk_config file:"
            cat $conffile
        fi
        echo ""
        task_error 710
        sendmon "TASKERROR partition 21"
        die "Partitioning tool did not create $LOGDIR/disk_var.sh file."
    fi
    # now define variable for root and boot partition and boot device
    . $LOGDIR/disk_var.sh
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    none
# Requires-Var:    $NFSROOT
# Suggests-Var:
# Short-Description: <task desc.>
### END SUBROUTINE INFO

call_debootstrap() {

    local dversion=$(dpkg-query -Wf '${Version}\n' debootstrap)
    echo "Creating base system using debootstrap version $dversion"

    echo "Calling debootstrap $FAI_DEBOOTSTRAP_OPTS $1 $FAI_ROOT $2 $3"
    LC_ALL=C debootstrap $FAI_DEBOOTSTRAP_OPTS $1 $FAI_ROOT $2 $3
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    none
# Requires-Var:    $FAI_ROOT $NFSROOT $LOGDIR
# Suggests-Var:    $FAI_DEBOOTSTRAP
# Short-Description: extrace minimal base file into target
### END SUBROUTINE INFO

task_extrbase() {

    local fs=$FAI_ROOT/etc/fstab

    fetch-basefile
    # remember, ftar extracts into $FAI_ROOT by default, so / means $FAI_ROOT/
    # copy the base file class based if it exists
    [ -d $FAI/basefiles ] && ftar -1v -s $FAI/basefiles /
    if [ $? -ne 0 ]; then
        if inside_nfsroot; then
            ftar -1v -c base -s /var/tmp /
        else
            [ -d $NFSROOT/var/tmp ] &&
               ftar -1v -c base -s $NFSROOT/var/tmp /
        fi

        # if no base file was extracted, call debootstrap
        if [ ! -d $FAI_ROOT/etc ]; then
            echo "No base file found. Calling debootstrap."
            [ -z "$FAI_DEBOOTSTRAP" ] && die "\$FAI_DEBOOTSTRAP undefined. Aborting"
            call_debootstrap $FAI_DEBOOTSTRAP
            task_error 801 $?
        fi
    fi

    # now we can copy fstab
    [ -f $LOGDIR/fstab ] && cp -S.old -bp $LOGDIR/fstab $fs
    # copy crypttab, if setup-storage created one
    [ -f $LOGDIR/crypttab ] && cp -p $LOGDIR/crypttab $FAI_ROOT/etc/crypttab
    # make /var/lib/dpkg a ramdisk
    mkramdisk -a
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:
# Requires-Var:    $FAI_ROOT $MNTPOINT $romountopt
# Suggests-Var:    $FAI_DEBMIRROR $debug
# Short-Description: mount Debian mirror via NFS
### END SUBROUTINE INFO

mount_mirror() {

    # mount debian mirror directory
    [ "$FAI_DEBMIRROR" ] || return   # nothing to do
    mkdir -p ${FAI_ROOT}${MNTPOINT}
    if mount $romountopt $FAI_DEBMIRROR ${FAI_ROOT}${MNTPOINT}; then
      [ "$debug" ] && echo "Mirror mounted from $FAI_DEBMIRROR to ${FAI_ROOT}${MNTPOINT}"
    else
      sendmon "TASKERROR repository $?"
      die "Can't mount $FAI_DEBMIRROR"
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_debconf () {

    if [ ! -d $FAI/debconf ]; then
        echo "Can't find debconf directory $FAI/debconf. Skipping preseeding."
        task_error 2
        return
    fi
    fai-debconf $FAI/debconf
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
### BEGIN SUBROUTINE INFO
# Provides-Var:    none
# Requires-Var:    $classes $FAI $FAI_ROOT $FAI_ETC_DIR $FAI_ALLOW_UNSIGNED
# Suggests-Var:
# Short-Description: prepare access to package repository
### END SUBROUTINE INFO

task_repository () {

    local v
    if [ X$verbose = X1 ]; then
	v=-v
    fi

    inside_nfsroot && FAI_ETC_DIR=/etc

    # some generale network files are needed on the client
    # resolv.conf is needed, /etc/hosts is useful in /target
    # use a file from the config space or from /etc inside the nfsroot
    if ! fcopy -BM $v /etc/resolv.conf ; then
        if [ -d "$FAI_ROOT/run/systemd/resolve" ]; then
            cp $v /etc/resolv.conf "$FAI_ROOT/run/systemd/resolve/stub-resolv.conf"
        fi
        if [ "$(readlink -f /etc/resolv.conf)" = "$(readlink -f $FAI_ROOT/etc/resolv.conf)" ] ; then
            :
	    # no need for copy since both are pointing to the same file
        else
            [ -f /etc/resolv.conf ] && cp $v /etc/resolv.conf $FAI_ROOT/etc
        fi
    fi
    if ! fcopy -M $v /etc/hosts ; then
	[ X$FAI_ROOT = X/ ] || cp $v -bS.orig /etc/hosts $FAI_ROOT/etc
    fi

    # apt specific things
    if ! fcopy -SBM $v /etc/apt/sources.list; then
	[ -f $FAI_ETC_DIR/apt/sources.list ] && cp $v $FAI_ETC_DIR/apt/sources.list $FAI_ROOT/etc/apt
    fi
    if ! fcopy -BM $v /etc/apt/preferences; then
        [ -f $FAI_ETC_DIR/apt/preferences ] && cp $v $FAI_ETC_DIR/apt/preferences $FAI_ROOT/etc/apt
    fi
    fcopy -SBMir /etc/apt # copy all other apt config files from the config space

    if [ X$FAI_ALLOW_UNSIGNED = X1 ]; then
        cat <<EOF > $FAI_ROOT/etc/apt/apt.conf.d/10fai
APT::Get::AllowUnauthenticated "true";
Acquire::AllowInsecureRepositories "true";
Aptitude::CmdLine::Ignore-Trust-Violations yes;
EOF
    else
	rm -f $FAI_ROOT/etc/apt/apt.conf.d/10fai
    fi

    # add apt keys for all classes
    for keyfile in ${classes:-}; do
        if [  -f $FAI/package_config/$keyfile.gpg ]; then
	    echo "Copying APT key $keyfile.gpg to target"
	    cp -v --preserve=timestamp $FAI/package_config/$keyfile.gpg $FAI_ROOT/etc/apt/trusted.gpg.d/
        fi
    done

    # mount Debian mirror via NFS if needed
    mount_mirror
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_updatebase() {

    # maybe the base system is not up to date

    if [ X$verbose = X1 ]; then
        updatebase </dev/null |& tee -a $LOGDIR/software.log
        task_error 474 ${PIPESTATUS[0]}
    else
        updatebase </dev/null >> $LOGDIR/software.log 2>&1
        task_error 474 $?
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_instsoft() {

    if [ X$verbose = X1 ]; then
	echo "Installing software may take a while"
    fi
    if [ "$debug" ]; then
        install_packages | tee -a $LOGDIR/software.log
        task_error 471 ${PIPESTATUS[0]}
    elif [ X$verbose = X1 ]; then
        install_packages </dev/null |& tee -a $LOGDIR/software.log
        task_error 471 ${PIPESTATUS[0]}
    else
        install_packages </dev/null >> $LOGDIR/software.log 2>&1
        task_error 471 $?
    fi
    # This almost indicates an error
    grep -E "^E:" $LOGDIR/software.log && task_error 472
    grep "Couldn't find any package whose name or description matched" $LOGDIR/software.log && task_error 321
    grep -q "E: Sub-process /usr/bin/dpkg returned an error code" $LOGDIR/software.log && task_error 620

    install_pkgs $FAI/pkgs |& tee -a $LOGDIR/software.log
    install_pkgs /media/data/pkgs |& tee -a $LOGDIR/software.log
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_finish() {

    mkramdisk -au # umount ramdisk
    if inside_nfsroot; then
        # show some local information
        df -PTh | grep -E ':|^/|^Filesystem'
        # show rx and tx bytes of network device
        grep . /sys/class/net/*/statistics/*x_bytes | perl -ane 'm#/sys/class/net/(.+)/statistics/(.+):(\d+)# && ($3) && ($1 ne lo) && printf "%s %s %.2f Mbytes\n",$1,$2,$3/1000000 '
        swapoff -a
    else
        df -PTh | grep -E "^Filesystem|$target"
    fi

    # undo fake of all programs made by fai
    fai-divert -R
    rm -f $FAI_ROOT/etc/apt/apt.conf.d/{10,90}fai
    echo -n "FAI finished at: ";date
    echo "The $FAI_ACTION took $(($(cut -d . -f 1 /proc/uptime) - start_seconds)) seconds."
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
task_chboot() {

    # the whole subroutine may be an externel script

    [ -z "$LOGUSER" ] && return # silently return from subroutine

    # do not execute for fai-diskimage
    if [[ $FAI_ROOT =~ '/fai-diskimage.' ]]; then
        return
    fi

    local frsh remotesh hostname
    local doexit=0
    read hostname < /proc/sys/kernel/hostname
    local ipaddr=$(grep IPADDR $LOGDIR/boot.log | cut -d= -f2 | sed "s/'//g")
    local nexttest=$(grep -E -s ^NEXTTEST= $LOGDIR/test.log | cut -d= -f2)

    case "$FAI_LOGPROTO" in
        ftp) remotesh=ssh ;;
        ssh) remotesh=ssh ;;
        rsh) remotesh=rsh ;;
        none) return ;;
	*) echo "ERROR: Unknown value for \$FAI_LOGPROTO." ;;
    esac
    frsh="$remotesh -l $LOGUSER ${SERVER}"

    if [ -z "$SERVER" ] ; then
        echo "SERVER not defined. Can't change network boot configuration"
        task_error 2
        doexit=1
    fi
    [ $doexit -eq 1 ] && return

    # change boot device (local disk or network) when using PXE
    # first test if rsh to server works
    $frsh true >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        task_error 3
        echo "WARNING: $frsh failed. Can't call fai-chboot on the install server." >&2
    else
        if [ -n "$nexttest" ]; then
                # for test sequences, we want the system to reinstall immediately with
                # with different class setup
            $frsh /usr/sbin/fai-chboot -k ADDCLASSES=$nexttest -FIv $ipaddr $MAC
        else
                # remove pxe config, so host will use default and boot from local disk
            $frsh /usr/sbin/fai-chboot -vd $ipaddr $MAC
        fi
    fi
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sendmon() {

    # send message to monitor daemon
    echo "$*" >> $LOGDIR/fai-monitor.log
    [ "$faimond" -eq 0 ] && return 0
    echo "$sendid $*" | nc -w 8 $monserver $FAI_MONITOR_PORT 2>/dev/null
    return $?
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
all_disks_and_size() {

    # print a list of devices and their block size
    ( grep -E 'md[0-9]{3,}$' /proc/partitions;
      grep -E ' etherd/e[[:digit:]]+\.[[:digit:]]+\b| i2o/hd.+\b| cciss/c[[:digit:]]+d[[:digit:]]+\b| ida/c[[:digit:]]+d[[:digit:]]+\b| rd/c[[:digit:]]+d[[:digit:]]+\b| fio.\b| hd.\b| sd[a-z]{1,2}\b|/disc\b| vd.\b| xvd.\b| nvme[[:digit:]]+n[[:digit:]]+$| mmcblk[[:digit:]]+$' /proc/partitions ) | awk '{ print $4 " " $3 }'
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
disks_by_id() {

    # list all disks by ID and their link to the device name
    (
      cd /dev/disk/by-id
      find -type l -printf "%f %l\n" | \
          grep -Pv '^md|-part\d|^wwn-|^nvme-eui|^nvme-nvme|^nvme\S+_\d ' | \
          grep -E '^ata|^nvme|^usb'
    )
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
checkdisk() {

    # read lines with device name and size and check if device is a disk
    # $1 can be a device name that will be ignored. Used for the device
    # name of the USB stick if we boot from it

    local igndev=$1 # a device to ignore
    local isdisk
    while read device blocks; do
        isdisk=1
        # old way of detecting disks: [ $(cat /sys/block/$device/removable) -eq 1 ] && isdisk=0
        [ $(stat -c %G /dev/$device) = "disk" ] || isdisk=0

        # if set, ignore this device
        [ "$device" = "$igndev" ] && isdisk=0
        if [ $isdisk -eq 1 ]; then
            echo "$device"
        fi
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
once_only() {

    # read from stdin and echo lines only once, ignore if the same line occurs again
    local line
    local -A seen
    while read line; do
        if [ X${seen["$line"]} = X ]; then
            echo $line
        fi
        seen["$line"]=1
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
all_disks_by_size() {
    all_disks_and_size | sort -nr -k2 | checkdisk $FAI_BOOTSTICK
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
largestdisk()  {
    all_disks_and_size | sort -nr -k2 | checkdisk $FAI_BOOTSTICK | head -1
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
smallestdisk() {
    all_disks_and_size | sort -n  -k2 | checkdisk $FAI_BOOTSTICK | head -1
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
matchdisks() {
    # matchdisks PATTERN PATTERN ....
    # create an ordered list of disks matching the patterns provided
    # pay attention that the pattern do not match multiple times
    grep_disks "$@" | sed -e 's#.*/##g'
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
notmatchdisks() {
    grepv_disks $1 | sed -e 's#.*/##g'
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
grep_disks() {
    if [ -z "$*" ]; then
        set -- '.'
    fi
    for p in "$@"; do
        disks_by_id | grep -E $p
    done
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
grepv_disks() {
    if [ -z "$1" ]; then
        set -- '.'
    fi
    disks_by_id | grep -E -v $1
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
set_bootstick() {

    # determine the device of the USB stick we boot from
    # sets the variable FAI_BOOTSTICK

    FAI_BOOTSTICK=$(mount | awk '/run\/initramfs\/live/ {print $1}')
    FAI_BOOTSTICK=${FAI_BOOTSTICK#/dev/}
    FAI_BOOTSTICK=${FAI_BOOTSTICK%%[[:digit:]]*}

    # use disk label FAI_CD to detect the USB stick
    if [ -z "$FAI_BOOTSTICK" ] && [ -L /dev/disk/by-label/FAI_CD ]; then
        FAI_BOOTSTICK=$(readlink /dev/disk/by-label/FAI_CD | sed -e 's#.*/##g')
        FAI_BOOTSTICK=${FAI_BOOTSTICK%%[[:digit:]]*}
    fi
}
