Re: Progressbar for extracting Basefiles
Karsten Nordsiek <[email protected]>
| Newsgroups | gmane.linux.debian.fai |
|---|---|
| Message-ID | <[email protected]> |
Please find included the both files I have changed. Hint: you need to install pv in the nfsroot Am 18.01.23 um 15:19 schrieb Karsten Nordsiek: > Yes that's working. > > I have added the -f Option > Now it works as fine as it should > > thx for the hint. > > Regards > > > Karsten Nordsiek > > > Am 18.01.23 um 14:34 sch > rieb Thomas Lange: >>>>>>> On Wed, 18 Jan 2023 12:28:26 +0100, Karsten Nordsiek >>>>>>> <[email protected]> said: >> > -1 Use only first tar file matching >> class name. >> > -c class[class] Define classes (space separated). >> Hmmm, it's hard to see what you have changed. A diff or git diff would >> be much better to understand. >> >> My first guess is to add -f to the pv call. >> >> -f, --force output even if standard error is not a >> terminal >> >> Inside FAI we redirect the output to a pipe which also does tee into a >> file. Or you may try to force the output (does pv use stderr?) to >> /dev/ttyN. >>
ftar
(text/plain, 5.2 KB)
#! /bin/bash #********************************************************************* # # ftar -- extract tar files using FAI classes # # This script is part of FAI (Fully Automatic Installation) # Copyright (C) 2001-2017 Thomas Lange, [email protected] # 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., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #********************************************************************* # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - die() { local e=$1 # first parameter is the exit code shift echo "ftar: $@" >&2 # print error message exit $e } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - extract() { local file=$1 local catname=$2 if [ $deletefiles -eq 1 ] ; then cd $target/$dir && rm -f -- .* * 2>/dev/null deletefiles=0 fi if [ $removedir -eq 1 ]; then cd $target/$dir || die 4 "ERROR: cd to $target/$dir failed. Aborting." [ $PWD = "/" ] && die 3 "WARNING: Will not do recursive removal of directory /" rm -rf -- .* * 2>/dev/null removedir=0 fi if [ $progress -eq 1 ]; then echo "ftar: extracting $file to $target/$dir" | tr -s '/' $catname $file | pv -f -s $(du -sb $file | awk '{print $1}') | tar $xattrs --numeric-owner -C $target/$dir $vflag -xf - else echo "ftar: extracting $file to $target/$dir" | tr -s '/' $catname $file | tar $xattrs --numeric-owner -C $target/$dir $vflag -xf - tardone=1 # if option -1 is set, only one class will be used [ $single -eq 1 ] && exit 0 fi } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - usage() { cat <<EOF ftar, extract tar files using classes. Copyright (C) 2001-2017 by Thomas Lange Usage: ftar [OPTION] ... SOURCE -1 Use only first tar file matching class name. -c class[class] Define classes (space separated). -d Delete all files in target before extracting. -D Create debug output. -h Show summary of options. -p Show progress indicator -r Recursively remove files in target before extracting. -s source_dir Look for source files relative to source_dir. -t target_dir Extract files relativ to target_dir. -v Be verbose. Not yet used. EOF exit 0 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - source=$FAI/files target=$FAI_ROOT deletefiles=0 removedir=0 tardone=0 single=0 ignore=0 progress=0 if [ -n "$classes" ]; then [ -f $LOGDIR/FAI_CLASSES ] && classes=$(< $LOGDIR/FAI_CLASSES) fi echo $@ while getopts 1hDdrvs:t:c:i:p opt ; do case "$opt" in d) deletefiles=1 ;; D) debug=1 ;; i) ignore=1 ;; r) removedir=1 ;; # v) verbose=1 ;; 1) single=1 ;; s) source=$OPTARG ;; t) target=$OPTARG ;; c) classes=$OPTARG ;; p) progress=1 ;; h) usage ;; esac done shift $((OPTIND - 1)) [ "$1" ] || usage # detect target fstype targetfstype=$(awk -vt="$target" '{if($2==t){print $3}}' /proc/mounts) if [ "$targetfstype" != "nfs" ]; then xattrs="--xattrs --xattrs-include=*.* --selinux --acl" else xattrs= fi # last class has highest priority # reverse order of classes for class in $classes; do revclasses="$class $revclasses" done [ "$debug" ] && vflag="-v" [ "$debug" ] && echo "ftar: classes : $revclasses" [ -z "$source" ] && die 1 "Source directory undefined." [ -z "$target" ] && die 1 "Target directory undefined." # currently only one directory is used dir=$1 fpath=$source/$dir [ -d $fpath ] || die 1 "No directory $fpath" for c in $revclasses ; do # what if a directory exists which is equal to the hostname or a classname? [ -f $fpath/$c.tgz ] && extract $fpath/$c.tgz zcat [ -f $fpath/$c.tar ] && extract $fpath/$c.tar cat [ -f $fpath/$c.tar.gz ] && extract $fpath/$c.tar.gz zcat [ -f $fpath/$c.tar.bz2 ] && extract $fpath/$c.tar.bz2 bzcat [ -f $fpath/$c.tar.xz ] && extract $fpath/$c.tar.xz xzcat [ -f $fpath/$c.txz ] && extract $fpath/$c.txz xzcat done if [ $tardone -eq 0 ]; then if [ $ignore -eq 1 ]; then echo "ftar: No matching class found in $fpath" exit 0 else die 1 "No matching class found in $fpath" fi fi exit 0
subroutines
(text/plain, 39.3 KB)
#! /bin/bash #********************************************************************* # # subroutines -- useful subroutines for FAI # # This script is part of FAI (Fully Automatic Installation) # (c) 2000-2021 by Thomas Lange, [email protected] # Universitaet zu Koeln # (c) 2001-2005 by Henning Glawe, [email protected] # 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 [ $do_init_tasks -eq 1 ] ; then exec bash -i else exit 99 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 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ### BEGIN SUBROUTINE INFO # Provides-Var: none # Required-Var: $classes # Short-Description: test if one or multiple classes are defined ### END SUBROUTINE INFO ifclass() { if [ $# -eq 1 ]; then _ifclass $1 return $? fi case $1 in -a) shift ; _ifclass_and $* ; return $? ;; -o) shift ; _ifclass_or $* ; return $? ;; esac } _ifclass() { # check one single class [ "${debug:-}" ] && echo "Test if class $1 is in ${classes:-}" >&2 # test if a class is defined local ret if [[ "${classes:-}" =~ (^|[[:space:]]+)$1($|[[:space:]]+) ]]; then ret=0 else ret=1 fi [ "${debug:-}" ] && echo "ifclass returns $ret" >&2 return $ret } _ifclass_and() { # check if all classes are defined (logical AND) local cl for cl in $*; do ifclass $cl || return 1 done return 0 } _ifclass_or() { # check if one of the classes is defined (logical OR) local cl for cl in $*; do ifclass $cl && return 0 done return 1 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - rwmount() { # remount partition read/write, for interactive use only mount -o rw,remount $1 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - save_dmesg() { [ $do_init_tasks -eq 0 ] && 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 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - umount_target() { if [ "$target" != '/' ]; then # do not umount during softupdate umount $FAI_ROOT/proc $FAI_ROOT/sys/firmware/efi/efivars $FAI_ROOT/sys $FAI_ROOT/dev/pts $FAI_ROOT/dev 2>/dev/null for dir in $(mount | grep $target | grep -E -v "media/mirror|tmpfs"| awk '{print $3}' | sort -r); do 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 vgchange -an $v echo vgremove -f $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 "WARNING: 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 # deprecated if [ -x $hfile.source ]; then echo "Source hook: $hook.$cl.source" sendmon "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 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: $DOMAIN $do_init_tasks # 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 [ $do_init_tasks -eq 1 ] ; then # set the system time and date using rdate or/and ntpdate [ "$TIMESRVS_1" ] && rdate $TIMESRVS_1 [ "$NTPSRVS_1" ] && ntpdate -b $NTPSRVS [ "$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 } # 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 [ $do_init_tasks -eq 1 ]; 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 [ $do_init_tasks -eq 1 -a 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 unset class svar # 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 cd $odir } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - task_mountdisks() { [ ! -f $LOGDIR/fstab ] && die "No $LOGDIR/fstab created." # mount swap space if [ $do_init_tasks -eq 1 ]; 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 [ $do_init_tasks -eq 1 ] && ! 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() { 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 fai-savelog -r task_error 410 $? } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ### BEGIN SUBROUTINE INFO # Description: Always called as last FAI task ### END SUBROUTINE INFO task_faiend() { local dir cdromdevice : ${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 [ $do_init_tasks -eq 1 -a "$flag_reboot" -eq 0 -a "$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 [ $do_init_tasks -eq 0 ]; then echo "Log files are saved in $LOGDIR" return 0 fi killall -q sshd systemd-udevd rsyslogd 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) xstamp=${FAI_ROOT//\//=} stamp=/var/run/fai/dirinstall-$xstamp unset xstamp 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 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 [ $do_init_tasks -eq 1 ] ; 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 if [ -x /usr/sbin/rsyslogd ]; then /usr/sbin/rsyslogd fi 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 == "/live/image") print $1;}' /proc/mounts | cut -d ':' -f 1) if [ -n "$SERVER" ]; then echo "Setting SERVER=$SERVER. Value extracted from NFSROOT." 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 [ $do_init_tasks -eq 1 ] && [ ! -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() { if [ X$USE_SETUP_STORAGE = X1 ]; then echo "WARNING: The variable USE_SETUP_STORAGE is not needed any more." >&2 echo "setup-storage is now the default tool for partitioning." fi [ ! -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 targetarch local dversion=$(dpkg-query -Wf '${Version}\n' debootstrap) echo "Creating base system using debootstrap version $dversion" # Check if we need cross architecture debootstrap targetarch=$(expr "$FAI_DEBOOTSTRAP_OPTS" : '.*--arch=\([^[:space:]]*\)' || true) hostarch1=$(dpkg --print-architecture) _debootstrap=qemu-debootstrap if [ -z "$targetarch" ]; then _debootstrap=debootstrap targetarch=$hostarch1 else if [ $targetarch = $hostarch1 ]; then _debootstrap=debootstrap fi fi for arch in $(dpkg --print-foreign-architectures); do if [ $targetarch = $arch ]; then _debootstrap=debootstrap break fi done if [ $_debootstrap = "qemu-debootstrap" ]; then if ! command -v $_debootstrap >&/dev/null; then die 1 "qemu-debootstrap not found. Please install the package qemu-user-static." fi fi echo "Calling $_debootstrap $FAI_DEBOOTSTRAP_OPTS $1 $FAI_ROOT $2" LC_ALL=C $_debootstrap $FAI_DEBOOTSTRAP_OPTS $1 $FAI_ROOT $2 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ### BEGIN SUBROUTINE INFO # Provides-Var: none # Requires-Var: $FAI_ROOT $do_init_tasks $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 -p -1v -s $FAI/basefiles / if [ $? -ne 0 ]; then if [ $do_init_tasks -eq 1 ]; then ftar -p -1v -c base -s /var/tmp / else [ -d $NFSROOT/var/tmp ] && ftar -p -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 check if this is a cross architecture installation, and install qemu user static if needed check-cross-arch $FAI_ROOT # 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 () { # check for old hooks for task prepareapt local hook cl hfile hook=prepareapt for cl in ${classes:-}; do hfile=$FAI/hooks/$hook.$cl if [ -f $hfile ]; then echo "WARNING: The task prepareapt is obsolete. It was renamed to task repository. Please rename your hook $hfile to repository.$cl." >&2 continue fi if [ -f $hfile.source ]; then echo "WARNING: The task prepareapt is obsolete. It was renamed to task repository. Please rename your hook $hfile.source to repository.$cl.source." >&2 continue fi done if [ X$verbose = X1 ]; then v=-v fi [ $do_init_tasks -eq 1 ] && 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 [ "$(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/ continue fi [ ! -f $FAI/package_config/$keyfile.asc ] && continue echo "Copying APT key from $keyfile.asc to $keyfile.asc.gpg " rm -f $FAI_ROOT/etc/apt/trusted.gpg.d/$keyfile.asc.gpg gpg --batch -o $FAI_ROOT/etc/apt/trusted.gpg.d/$keyfile.asc.gpg --dearmor $FAI/package_config/$keyfile.asc 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 } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - task_finish() { mkramdisk -au # umount ramdisk if [ $do_init_tasks -eq 1 ] ; 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 local frsh remotesh local doexit=0 local hostname 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 ;; *) 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 $? } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -