497 lines
11 KiB
Bash
Executable File
497 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
## Sequencer script is doing nothing on its own. It is included by a squence script
|
|
## which uses the sequencer.sh to provide sequencial operations with or without
|
|
## user interaction (see seqTemplate.sh)
|
|
|
|
## Version information
|
|
|
|
VERSION_REV=4
|
|
VERSION_MAJOR=0
|
|
VERSION_MINOR=0
|
|
|
|
## Start of generic script part
|
|
|
|
QUIET=0
|
|
DRY=0
|
|
VERBOSE=0
|
|
ERNO=0
|
|
MAX_STEP=255
|
|
ALIAS=
|
|
TEMPLATE_NAME=seqTemplateExample.sh
|
|
MISSING_CONF=missingConf.log
|
|
VERSION_STRING="${VERSION_REV}.${VERSION_MAJOR}.${VERSION_MINOR}"
|
|
|
|
helpSequencer() {
|
|
echo "Usage: ${0##*/} [OPTIONS] [STEP NUMBER(s) or ALIAS]"
|
|
echo
|
|
echo " [OPTIONS]"
|
|
echo " --dry-run, -d : Only print to console what would be done"
|
|
echo " ! Attention - Sequence must support this"
|
|
echo " --help, -h : Display help"
|
|
echo " --quiet, -q : Don't ask for permission to execute steps"
|
|
echo " If called without starting step number, only this help is shown"
|
|
echo " --verbose, -v : Verbose output (use exe() function to call shell commands in seqs)"
|
|
echo " ( e.g.: exe apt update )"
|
|
echo " --version : Display version of sequencer and revision of sequence"
|
|
echo
|
|
echo " [STEP NUMBER(s) 1-${MAX_STEP} or ALIAS]"
|
|
echo " Single STEP or ALIAS : starting point of process"
|
|
echo " Multiple STEPS or ALIAS : execute only given steps"
|
|
echo " execute only one step with using special step 0"
|
|
echo " ( e.g. only execute step 4: $0 4 0 )"
|
|
}
|
|
|
|
# endCheckEmpty [VariableName] [DESCRIPTION]
|
|
# DESCRIPTION : Optional text for error
|
|
endCheckEmpty() {
|
|
local errorText=$1
|
|
eval 'local ref=$'$1
|
|
|
|
if [ ! -z "$2" ] ; then
|
|
errorText=$2
|
|
fi
|
|
if [ -z $ref ] ; then
|
|
echo -e "[Error] $errorText must not be empty.\nAborting installation."
|
|
exit 666
|
|
fi
|
|
}
|
|
|
|
existsFunction() {
|
|
local NOTFOUND=0
|
|
declare -F $1 &>>/dev/null || NOTFOUND=1
|
|
return $NOTFOUND
|
|
}
|
|
|
|
saveReturn() {
|
|
if [ $1 -ne 0 ] ; then
|
|
ERNO=$1
|
|
fi
|
|
}
|
|
|
|
# endReturn [-f]
|
|
# -f : force exit with $ERNO without user input
|
|
endReturn() {
|
|
if [[ ( $ERNO -ne 0 && $QUIET -ne 0 ) || ( $ERNO -ne 0 && ! -z $1 && $1 == "-f" ) ]] ; then
|
|
echo
|
|
echo -e "[Error] Return value $ERNO detected.\nAborting installation."
|
|
exit $ERNO
|
|
fi
|
|
if [ $ERNO -ne 0 ] ; then
|
|
echo
|
|
echo "[Error] Return value $ERNO detected."
|
|
read -p "End installation: y(default)/n? " answer
|
|
case $answer in
|
|
[nN])
|
|
echo
|
|
echo Continuing installation...
|
|
;;
|
|
*)
|
|
echo
|
|
echo Installation aborted
|
|
exit $ERNO;
|
|
;;
|
|
esac
|
|
fi
|
|
}
|
|
|
|
# addConf <CONF_MODE> <CONFIGTEXT> <CONFIGFILE>
|
|
# trying to write a file
|
|
# if exists, one attempt is made to create bck file of it
|
|
# if all fails, a log file is created with the conflicts to be resolved by the user
|
|
addConf() {
|
|
local confMode=""
|
|
case "$1" in
|
|
-c) # create a new file
|
|
confMode="-c"
|
|
;;
|
|
-a) # append to existing file
|
|
confMode="-a"
|
|
;;
|
|
-s) # skip if CONFIGFILE exists
|
|
confMode="-s"
|
|
;;
|
|
-m) # only add content to missing conf and warn user
|
|
confMode="-m"
|
|
;;
|
|
*) # default
|
|
echo "Parameter 1 (-a|-c|-m|-s) missing for addConf()"
|
|
exit 0;
|
|
;;
|
|
esac
|
|
|
|
if [ "$DRY" -ne 0 ] ; then
|
|
echo "-- Writing $3...dry-run"
|
|
return 0;
|
|
fi
|
|
echo -n "Writing $3..."
|
|
|
|
if [ $confMode != "-m" ] ; then
|
|
# try writing config directly
|
|
if [ ! -f "$3" ] ; then
|
|
case "$confMode" in
|
|
-c|-s)
|
|
echo "$2" > "$3"
|
|
;;
|
|
-a)
|
|
echo "$2" >> "$3"
|
|
;;
|
|
esac
|
|
echo "ok"
|
|
return 0
|
|
fi
|
|
|
|
if [ $confMode == "-s" ] ; then
|
|
# if skip is selected, don't try to backup but add confilict entry
|
|
echo "skipping (exists)"
|
|
else
|
|
# try backup existing config
|
|
if [ ! -f "$3".bck ] ; then
|
|
cp -ar "$3" "$3".bck
|
|
if [ $confMode == "-c" ] ; then
|
|
echo "$2" > "$3"
|
|
else
|
|
echo "$2" >> "$3"
|
|
fi
|
|
echo -e "ok \n[WARN] Existing config saved to ${3}.bck"
|
|
return 0
|
|
fi
|
|
fi
|
|
else
|
|
echo "nok"
|
|
fi
|
|
|
|
# add configuration to missingConf file
|
|
if [ "$missingDate" = "" ] ; then
|
|
missingDate=set
|
|
echo -n "### " >> "$MISSING_CONF"
|
|
date >> "$MISSING_CONF"
|
|
fi
|
|
echo "#--- $3 ---" >> "$MISSING_CONF"
|
|
echo "$2" >> "$MISSING_CONF"
|
|
echo >> "$MISSING_CONF"
|
|
|
|
echo "[WARN] Check $(realpath "$MISSING_CONF") for configuration conflicts ($3)"
|
|
return 1
|
|
}
|
|
|
|
# execute [-q] <Step Number>
|
|
# -q: don't stop and don't report step functions which cannot be found
|
|
# execute given step_<Step Number> function
|
|
execute() {
|
|
local NOTFOUND=0
|
|
local NOREPORT=0
|
|
|
|
if [ $1 == "-q" ] ; then
|
|
NOREPORT=1
|
|
shift
|
|
fi
|
|
|
|
# check if step function exists
|
|
declare -F step_$1 &>>/dev/null || NOTFOUND=1
|
|
if [ $NOTFOUND -eq 1 ] && [ $NOREPORT -ne 1 ] ; then
|
|
echo "Step $1 not found"
|
|
exit 1;
|
|
fi
|
|
|
|
# don't execute step functions which are not available
|
|
if [ $NOTFOUND -eq 1 ] ; then
|
|
return $NOTFOUND
|
|
fi
|
|
|
|
echo -en "\n[STEP $1] "
|
|
existsFunction step_${1}_info
|
|
if [ $? -eq 0 ] ; then
|
|
step_${1}_info $1
|
|
fi
|
|
if [ $QUIET -ne 1 ] ; then
|
|
read -p "Start: y/n(default)? " answer
|
|
case $answer in
|
|
[yY])
|
|
step_$1 $1
|
|
;;
|
|
*)
|
|
echo Aborting installation at step $1
|
|
exit 1;
|
|
;;
|
|
esac
|
|
else
|
|
step_$1 $1
|
|
fi
|
|
}
|
|
|
|
# checkStep <Step Number or Alias>
|
|
# return 0 - for invalid step
|
|
# Check sanitiy of step number or
|
|
# Check if alias exists
|
|
checkStep() {
|
|
if (( $1 < 1 || $1 > $MAX_STEP )) ; then
|
|
eval 'local ref=$alias_'$1
|
|
if [ -z $ref ] || [ "$ref" == "$1" ] ; then
|
|
return 0
|
|
else
|
|
return $ref
|
|
fi
|
|
else
|
|
return $1
|
|
fi
|
|
}
|
|
|
|
# continous <Starting Step Number>
|
|
# (max $MAX_STEP)
|
|
# execute installation continously from given starting step
|
|
continous() {
|
|
local step=0
|
|
|
|
checkStep $1
|
|
step=$?
|
|
if [[ $step == 0 ]] ; then
|
|
return 1
|
|
fi
|
|
|
|
for ((i=$step; i<=${MAX_STEP}; i++)); do
|
|
execute -q $i
|
|
local res=$?
|
|
if [ $res -ne 0 ] ; then
|
|
break;
|
|
fi
|
|
done
|
|
}
|
|
|
|
# selection <Step Number List (separated by space)>
|
|
# execute given step list
|
|
# e.g.: selection -q 1 4 12
|
|
selection() {
|
|
local step=0
|
|
for i in $@ ; do
|
|
checkStep $i
|
|
step=$?
|
|
# stop on step 0
|
|
if [ $step -eq 0 ] ; then
|
|
break
|
|
else
|
|
execute $step
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Creating a minimal step definition template
|
|
createTemplate() {
|
|
if [ -f $TEMPLATE_NAME ] ; then
|
|
return 1
|
|
fi
|
|
echo "#!/bin/bash" > $TEMPLATE_NAME
|
|
echo >> $TEMPLATE_NAME
|
|
echo "# Step 1 is mandatory" >> $TEMPLATE_NAME
|
|
echo "step_1_info() { echo \"My custom step \$1\"; }" >> $TEMPLATE_NAME
|
|
echo "step_1_alias() { ALIAS=\"begin\"; }" >> $TEMPLATE_NAME
|
|
echo "step_1() {" >> $TEMPLATE_NAME
|
|
echo " echo \"Doing something...\"" >> $TEMPLATE_NAME
|
|
echo "}" >> $TEMPLATE_NAME
|
|
echo >> $TEMPLATE_NAME
|
|
echo "VERSION_SEQREV=${VERSION_REV}" >> $TEMPLATE_NAME
|
|
echo ". $0" >> $TEMPLATE_NAME
|
|
|
|
chmod +x $TEMPLATE_NAME
|
|
return 0
|
|
}
|
|
|
|
# Parse alias functions "step_[STEP NUBER]_alias" to create
|
|
# back reference variable of schema:
|
|
# alias_[ALIAS]=[STEP NUMBER]
|
|
parseAlias() {
|
|
for ((i=1; i<=${MAX_STEP}; i++)); do
|
|
# Check for alias definition
|
|
existsFunction step_${i}_alias
|
|
if [ $? -ne 0 ] ; then
|
|
continue
|
|
fi
|
|
|
|
# Function writes global ALIAS variable
|
|
step_${i}_alias
|
|
|
|
eval 'alias_'$ALIAS'='$i
|
|
done
|
|
}
|
|
|
|
# Always display sequencer help and, if available, sequence help
|
|
displayHelp() {
|
|
|
|
helpSequencer
|
|
|
|
# check if step definition exists by looking for step_1()
|
|
existsFunction step_1
|
|
if [ $? -ne 0 ] ; then
|
|
echo -e "\n It seems ${0##*/} was called directly."
|
|
echo -e " Please create a sequence script first.\n"
|
|
read -p " Create a template now? y/n(default)? " answer
|
|
case $answer in
|
|
[yY])
|
|
createTemplate
|
|
if [ $? -eq 0 ] ; then
|
|
echo -e "\n $TEMPLATE_NAME created."
|
|
else
|
|
echo -e "\n $TEMPLATE_NAME exists...Nothing to do!"
|
|
fi
|
|
;;
|
|
*)
|
|
echo -e "\n Nothing to do!"
|
|
;;
|
|
esac
|
|
exit 1;
|
|
else
|
|
echo " Step (= alias) documentation:"
|
|
for ((i=1; i<=${MAX_STEP}; i++)); do
|
|
|
|
# Display step reference in help if step function exists
|
|
existsFunction step_${i}
|
|
if [ $? -ne 0 ] ; then
|
|
continue
|
|
fi
|
|
printf ' Step %3s ' $i
|
|
|
|
# Display alias if exists
|
|
existsFunction step_${i}_alias
|
|
if [ $? -eq 0 ] ; then
|
|
step_${i}_alias
|
|
echo " = $ALIAS"
|
|
printf ' : '
|
|
else
|
|
echo -n " : "
|
|
fi
|
|
|
|
# Display step help only if info function exists
|
|
existsFunction step_${i}_info
|
|
if [ $? -eq 0 ] ; then
|
|
step_${i}_info $i
|
|
else
|
|
echo " - step_${i}_info() missing"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
# showVersion
|
|
showVersion() {
|
|
echo "Sequencer ${VERSION_STRING}"
|
|
echo -n "Seq Revision "
|
|
if [ ! -z "${VERSION_SEQREV}" ] ; then
|
|
echo "${VERSION_SEQREV}"
|
|
else
|
|
echo "-"
|
|
fi
|
|
}
|
|
|
|
exe() {
|
|
local arr=("$@")
|
|
if [ $DRY -ne 0 ] ; then
|
|
echo "-- ${arr[@]}"
|
|
elif [ $VERBOSE -eq 1 ] ; then
|
|
(set -x; "${arr[@]}")
|
|
else
|
|
"${arr[@]}"
|
|
fi
|
|
}
|
|
|
|
# Handle dry run and verbose output for commands containing pipe and/or redirects
|
|
# exep <COMMAND TO RUN AS STRING>
|
|
exep() {
|
|
if [ $DRY -ne 0 ] ; then
|
|
echo "-- $1"
|
|
elif [ $VERBOSE -eq 1 ] ; then
|
|
echo "++ $1"
|
|
fi
|
|
|
|
if [ $DRY -eq 0 ] ; then
|
|
bash -c "$1"
|
|
fi
|
|
}
|
|
|
|
|
|
main() {
|
|
local START=0
|
|
|
|
# options check
|
|
for arg in "$@" ; do
|
|
case "$arg" in
|
|
--dry-run|-d) # shows what would be done
|
|
DRY=1
|
|
QUIET=1
|
|
shift
|
|
;;
|
|
--help|-h) # show only help
|
|
displayHelp
|
|
exit 0;
|
|
;;
|
|
--quiet|-q) # detect if option quiet is available
|
|
QUIET=1
|
|
shift
|
|
;;
|
|
--verbose|-v) # set verbose flag
|
|
VERBOSE=1
|
|
shift
|
|
;;
|
|
--version) # version request
|
|
showVersion
|
|
exit 0;
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z $1 ] || [ "$1" == "" ] ; then
|
|
# Empty -> show help
|
|
displayHelp
|
|
# Assume starting at one for interactive mode
|
|
START=1
|
|
fi
|
|
|
|
# compatibility check of sequence
|
|
if [ ! -z $VERSION_SEQREV ] && [ $VERSION_SEQREV -gt $VERSION_REV ] ; then
|
|
echo "[ERROR] Unsupported sequence revision"
|
|
showVersion
|
|
exit 1
|
|
fi
|
|
# exclude older versions if needed
|
|
if [ ! -z $VERSION_SEQREV ] && [ $VERSION_SEQREV -lt 3 ] ; then
|
|
echo "[ERROR] Unsupported sequence revision (addConf)"
|
|
showVersion
|
|
exit 1
|
|
fi
|
|
if [ -z $VERSION_SEQREV ] ; then
|
|
echo -e "[WARNING] No sequence revision found. Trying anyway...\n";
|
|
fi
|
|
|
|
# check for starting step
|
|
if [ ! -z "$1" ] ; then
|
|
START=$1
|
|
else
|
|
# End here on quiet mode and no step was given
|
|
if [ $QUIET -eq 1 ] ; then
|
|
exit 1;
|
|
fi
|
|
fi
|
|
|
|
if [ $DRY -ne 0 ] ; then
|
|
echo
|
|
echo "[WARN] Dry run active."
|
|
echo " Printed commands may not be accurate (e.g. quotation incorrect)"
|
|
echo " Sequence may ignore dry run"
|
|
read -p "Press enter to continue or Ctrl + c to abort"
|
|
fi
|
|
|
|
parseAlias
|
|
|
|
# check if more than one step is given and select execution mode
|
|
if [ ! -z $2 ] ; then
|
|
selection $@
|
|
else
|
|
continous $START
|
|
fi
|
|
|
|
echo
|
|
echo "${0##*/} finished"
|
|
}
|
|
|
|
main "$@"
|
|
exit 0;
|