b427341d28
Do not create function with the same name as standard command, it is very confusing. Fixes QubesOS/qubes-issues#1174
484 lines
17 KiB
Bash
Executable File
484 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
|
# vim: set ts=4 sw=4 sts=4 et :
|
|
|
|
set -e
|
|
|
|
VERBOSE=${VERBOSE:-1}
|
|
DEBUG=${DEBUG:-0}
|
|
|
|
. ./functions-name.sh
|
|
|
|
################################################################################
|
|
# Global functions
|
|
################################################################################
|
|
# ------------------------------------------------------------------------------
|
|
# Define colors
|
|
# ------------------------------------------------------------------------------
|
|
colors() {
|
|
## Thanks to:
|
|
## http://mywiki.wooledge.org/BashFAQ/037
|
|
## Variables for terminal requests.
|
|
[[ -t 2 ]] && {
|
|
export alt=$( tput smcup || tput ti ) # Start alt display
|
|
export ealt=$( tput rmcup || tput te ) # End alt display
|
|
export hide=$( tput civis || tput vi ) # Hide cursor
|
|
export show=$( tput cnorm || tput ve ) # Show cursor
|
|
export save=$( tput sc ) # Save cursor
|
|
export load=$( tput rc ) # Load cursor
|
|
export bold=$( tput bold || tput md ) # Start bold
|
|
export stout=$( tput smso || tput so ) # Start stand-out
|
|
export estout=$( tput rmso || tput se ) # End stand-out
|
|
export under=$( tput smul || tput us ) # Start underline
|
|
export eunder=$( tput rmul || tput ue ) # End underline
|
|
export reset=$( tput sgr0 || tput me ) # Reset cursor
|
|
export blink=$( tput blink || tput mb ) # Start blinking
|
|
export italic=$( tput sitm || tput ZH ) # Start italic
|
|
export eitalic=$( tput ritm || tput ZR ) # End italic
|
|
[[ ${TERM} != *-m ]] && {
|
|
export red=$( tput setaf 1|| tput AF 1 )
|
|
export green=$( tput setaf 2|| tput AF 2 )
|
|
export yellow=$( tput setaf 3|| tput AF 3 )
|
|
export blue=$( tput setaf 4|| tput AF 4 )
|
|
export magenta=$( tput setaf 5|| tput AF 5 )
|
|
export cyan=$( tput setaf 6|| tput AF 6 )
|
|
}
|
|
export white=$( tput setaf 7|| tput AF 7 )
|
|
export default=$( tput op )
|
|
export eed=$( tput ed || tput cd ) # Erase to end of display
|
|
export eel=$( tput el || tput ce ) # Erase to end of line
|
|
export ebl=$( tput el1 || tput cb ) # Erase to beginning of line
|
|
export ewl=$eel$ebl # Erase whole line
|
|
export draw=$( tput -S <<< ' enacs
|
|
smacs
|
|
acsc
|
|
rmacs' || { \
|
|
tput eA; tput as;
|
|
tput ac; tput ae; } ) # Drawing characters
|
|
export back=$'\b'
|
|
} 2>/dev/null ||:
|
|
|
|
export build_already_defined_colors="true"
|
|
}
|
|
|
|
if [ ! "$build_already_defined_colors" = "true" ]; then
|
|
colors
|
|
fi
|
|
|
|
if [ "${VERBOSE}" -ge 2 -o "${DEBUG}" == "1" ]; then
|
|
chroot_cmd() {
|
|
# Display `chroot` or `systemd-nspawn` in blue ONLY if VERBOSE >= 2
|
|
# or DEBUG == "1"
|
|
local retval
|
|
true ${blue}
|
|
|
|
# Need to capture exit code after running chroot or systemd-nspawn
|
|
# so it will be available as a return value
|
|
if [ "${SYSTEMD_NSPAWN_ENABLE}" == "1" ]; then
|
|
systemd-nspawn $systemd_bind -D "${INSTALLDIR}" -M "${DIST}" ${1+"$@"} && { retval=$?; true; } || { retval=$?; true; }
|
|
else
|
|
/usr/sbin/chroot "${INSTALLDIR}" ${1+"$@"} && { retval=$?; true; } || { retval=$?; true; }
|
|
fi
|
|
true ${reset}
|
|
return $retval
|
|
}
|
|
else
|
|
chroot_cmd() {
|
|
if [ "${SYSTEMD_NSPAWN_ENABLE}" == "1" ]; then
|
|
systemd-nspawn $systemd_bind -D "${INSTALLDIR}" -M "${DIST}" ${1+"$@"}
|
|
else
|
|
/usr/sbin/chroot "${INSTALLDIR}" ${1+"$@"}
|
|
fi
|
|
}
|
|
fi
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Display messages in color
|
|
# ------------------------------------------------------------------------------
|
|
# Only output text under certain conditions
|
|
output() {
|
|
if [ "${VERBOSE}" -ge 1 ] && [[ -z ${TEST} ]]; then
|
|
# Don't echo if -x is set since it will already be displayed via true
|
|
[[ ${-/x} != $- ]] || echo -e ""$@""
|
|
fi
|
|
}
|
|
|
|
outputc() {
|
|
color=${1}
|
|
shift
|
|
output "${!color}"$@"${reset}" || :
|
|
}
|
|
|
|
info() {
|
|
output "${bold}${blue}INFO: "$@"${reset}" || :
|
|
}
|
|
|
|
debug() {
|
|
output "${bold}${green}DEBUG: "$@"${reset}" || :
|
|
}
|
|
|
|
warn() {
|
|
output "${stout}${yellow}WARNING: "$@"${reset}" || :
|
|
}
|
|
|
|
error() {
|
|
output "${bold}${red}ERROR: "$@"${reset}" || :
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Takes an array and exports it a global variable
|
|
#
|
|
# $1: Array to export
|
|
# $2: Global variable name to use for export
|
|
#
|
|
# http://ihaveabackup.net/2012/01/29/a-workaround-for-passing-arrays-in-bash/
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
setArrayAsGlobal() {
|
|
local array="$1"
|
|
local export_as="$2"
|
|
local code=$(declare -p "$array" 2> /dev/null || true)
|
|
local replaced="${code/$array/$export_as}"
|
|
eval ${replaced/declare -/declare -g}
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Checks if the passed element exists in passed array
|
|
# $1: Element to check for
|
|
# $2: Array to check for element in
|
|
#
|
|
# Returns 0 if True, or 1 if False
|
|
# ------------------------------------------------------------------------------
|
|
elementIn () {
|
|
local element
|
|
for element in "${@:2}"; do [[ "$element" == "$1" ]] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Spilts the path and returns an array of parts
|
|
#
|
|
# $1: Full path of file to split
|
|
# $2: Global variable name to use for export
|
|
# Returns:
|
|
# ([full]='original name' [dir]='directory' [base]='filename' [ext]='extension')
|
|
#
|
|
# Original concept path split from:
|
|
# https://stackoverflow.com/questions/965053/extract-filename-and-extension-in-bash
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
splitPath() {
|
|
|
|
local return_global_var=$2
|
|
local filename="${1##*/}" # Strip longest match of */ from start
|
|
local dir="${1:0:${#1} - ${#filename}}" # Substring from 0 thru pos of filename
|
|
local base="${filename%.[^.]*}" # Strip shortest match of . plus at least one non-dot char from end
|
|
local ext="${filename:${#base} + 1}" # Substring from len of base thru end
|
|
if [ "$ext" ]; then
|
|
local dotext=".$ext"
|
|
else
|
|
local dotext=""
|
|
fi
|
|
if [[ -z "$base" && -n "$ext" ]]; then # If we have an extension and no base, it's really the base
|
|
base=".$ext"
|
|
ext=""
|
|
dotext=""
|
|
fi
|
|
|
|
declare -A PARTS=([full]="$1" [dir]="$dir" [base]="$base" [ext]="$ext" [dotext]="$dotext")
|
|
setArrayAsGlobal PARTS $return_global_var
|
|
}
|
|
|
|
templateDirs() {
|
|
local template_flavor=${1-${TEMPLATE_FLAVOR}}
|
|
local match=0
|
|
|
|
# If TEMPLATE_FLAVOR_DIR is not already an array, make it one
|
|
if ! [[ "$(declare -p TEMPLATE_FLAVOR_DIR 2>/dev/null)" =~ ^declare\ -a.* ]] ; then
|
|
TEMPLATE_FLAVOR_DIR=( ${TEMPLATE_FLAVOR_DIR} )
|
|
fi
|
|
|
|
for element in "${TEMPLATE_FLAVOR_DIR[@]}"
|
|
do
|
|
# (wheezy+whonix-gateway / wheezy+whonix-gateway+gnome[+++] / wheezy+gnome )
|
|
if [ "${element%:*}" == "$(templateName ${template_flavor})" ]; then
|
|
eval echo -e "${element#*:}"
|
|
match=1
|
|
|
|
# Very short name compare (+proxy)
|
|
elif [ "${element:0:1}" == "+" -a "${element%:*}" == "+${template_flavor}" ]; then
|
|
eval echo -e "${element#*:}"
|
|
match=1
|
|
|
|
# Generic template directory that matches all flavors, or even no flavors
|
|
elif [ "${element:0:1}" == "*" ]; then
|
|
eval echo -e "${element#*:}"
|
|
match=1
|
|
fi
|
|
done
|
|
|
|
if [ "${match}" -eq 1 ]; then
|
|
return
|
|
fi
|
|
|
|
local template_flavor_prefix="$(templateFlavorPrefix ${template_flavor})"
|
|
if [ -n "${template_flavor}" -a "${template_flavor}" == "+" ]; then
|
|
local dir="${SCRIPTSDIR}/${template_flavor_prefix}"
|
|
elif [ -n "${template_flavor}" ]; then
|
|
local dir="${SCRIPTSDIR}/${template_flavor_prefix}${template_flavor}"
|
|
else
|
|
local dir="${SCRIPTSDIR}"
|
|
fi
|
|
|
|
echo "${dir}"
|
|
}
|
|
|
|
exists() {
|
|
filename="${1}"
|
|
|
|
if [ -e "${filename}" ] && ! elementIn "${filename}" "${GLOBAL_CACHE[@]}"; then
|
|
# Cache $script
|
|
#
|
|
# GLOBAL_CACHE is declared in the `getFileLocations` function and is later
|
|
# renamed to a name passed into the function as $1 to allow scripts using
|
|
# the function to have access to the array
|
|
GLOBAL_CACHE["${#GLOBAL_CACHE[@]}"]="${filename}"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
templateFile() {
|
|
local file="$1"
|
|
local suffix="$2"
|
|
local template_flavor="$3"
|
|
local template_dirs="$(templateDirs "${template_flavor}")"
|
|
|
|
splitPath "${file}" path_parts
|
|
|
|
for template_dir in ${template_dirs[@]}; do
|
|
# No template flavor
|
|
if [ -z "${template_flavor}" ]; then
|
|
if [ "${suffix}" ]; then
|
|
exists "${SCRIPTSDIR}/${path_parts[base]}_${suffix}${path_parts[dotext]}" || true
|
|
else
|
|
exists "${SCRIPTSDIR}/${path_parts[base]}${path_parts[dotext]}" || true
|
|
fi
|
|
return
|
|
fi
|
|
|
|
# Locate file in directory named after flavor
|
|
if [ "${suffix}" ]; then
|
|
# Append suffix to filename (before extension)
|
|
# `minimal` is the template_flavor being used in comment example
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/minimal/packages_qubes_suffix.list)
|
|
exists "${template_dir}/${template_flavor}/${path_parts[base]}_${suffix}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/minimal/packages_qubes_suffix.list)
|
|
exists "${template_dir}/${template_flavor}/${path_parts[base]}_${suffix}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/packages_qubes_suffix.list)
|
|
exists "${template_dir}/${path_parts[base]}_${suffix}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/packages_qubes_minimal_suffix.list)
|
|
exists "${template_dir}/${path_parts[base]}_${suffix}_${template_flavor}${path_parts[dotext]}" || true
|
|
|
|
# (SCRIPTSDIR/packages_qubes_minimal_suffix.list)
|
|
exists "${SCRIPTSDIR}/${path_parts[base]}_${suffix}_${template_flavor}${path_parts[dotext]}" || true
|
|
else
|
|
# (TEMPLATE_FLAVOR_DIR/minimal/packages_qubes.list)
|
|
exists "${template_dir}/${template_flavor}/${path_parts[base]}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/minimal/packages_qubes_minimal.list)
|
|
exists "${template_dir}/${template_flavor}/${path_parts[base]}_${template_flavor}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/packages_qubes.list)
|
|
exists "${template_dir}/${path_parts[base]}${path_parts[dotext]}" || true
|
|
|
|
# (TEMPLATE_FLAVOR_DIR/packages_qubes_minimal.list)
|
|
exists "${template_dir}/${path_parts[base]}_${template_flavor}${path_parts[dotext]}" || true
|
|
|
|
# (SCRIPTSDIR/packages_qubes_minimal.list)
|
|
exists "${SCRIPTSDIR}/${path_parts[base]}_${template_flavor}${path_parts[dotext]}" || true
|
|
fi
|
|
done
|
|
}
|
|
|
|
copyTreeExec() {
|
|
local source_dir="$1"
|
|
local dir="$2"
|
|
local template_flavor="$3"
|
|
local target_dir="$4"
|
|
|
|
local template_dirs="$(templateDirs ${template_flavor})"
|
|
|
|
for template_dir in ${template_dirs[@]}; do
|
|
local source_dir="$(readlink -m ${source_dir:-${template_dir}}/${dir})"
|
|
local target_dir="$(readlink -m ${target_dir:-${INSTALLDIR}})"
|
|
|
|
if ! [ -d "${source_dir}" ]; then
|
|
debug "No extra files to copy for ${dir}"
|
|
return 0
|
|
fi
|
|
|
|
debug "Copying ${source_dir}/* ${target_dir}"
|
|
cp -rp "${source_dir}/." "${target_dir}"
|
|
|
|
if [ -f "${source_dir}/.facl" ]; then
|
|
debug "Restoring file permissions..."
|
|
pushd "${target_dir}"
|
|
{
|
|
setfacl --restore="${source_dir}/.facl" 2>/dev/null ||:
|
|
rm -f .facl
|
|
}
|
|
popd
|
|
fi
|
|
done
|
|
}
|
|
|
|
callTemplateFunction() {
|
|
local calling_script="$1"
|
|
local calling_arg="$2"
|
|
local functionExec="$3"
|
|
local template_flavor="${TEMPLATE_FLAVOR}"
|
|
|
|
${functionExec} "${calling_script}" \
|
|
"${calling_arg}" \
|
|
"${template_flavor}"
|
|
|
|
# Find a $DIST sub-directory
|
|
${functionExec} "${calling_script}" \
|
|
"${calling_arg}" \
|
|
"+"
|
|
|
|
for option in ${TEMPLATE_OPTIONS[@]}
|
|
do
|
|
# Long name (wheezy+whonix-gateway+proxy)
|
|
${functionExec} "${calling_script}" \
|
|
"${calling_arg}" \
|
|
"${TEMPLATE_FLAVOR}+${option}"
|
|
|
|
# Short name (wheezy+proxy)
|
|
${functionExec} "${calling_script}" \
|
|
"${calling_arg}" \
|
|
"${option}"
|
|
done
|
|
|
|
# If template_flavor exists, also check on base distro
|
|
if [ -n "${template_flavor}" ]; then
|
|
${functionExec} "${calling_script}" \
|
|
"${calling_arg}"
|
|
fi
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Will return all files that match pattern of suffix
|
|
# Example:
|
|
# filename = packages.list
|
|
# suffix = ${DIST} (wheezy)
|
|
#
|
|
# Will look for a file name packages_wheezy.list in:
|
|
# the $SCRIPTSDIR; beside original
|
|
# the $SCRIPTSDIR/$DIST (wheezy) directory
|
|
# any included template module directories ($SCRIPTSDIR/gnome)
|
|
#
|
|
# All matches are returned and each will be able to be used
|
|
# ------------------------------------------------------------------------------
|
|
getFileLocations() {
|
|
local return_global_var=$1
|
|
local filename="$2"
|
|
local suffix="$3"
|
|
local function="templateFile"
|
|
|
|
unset GLOBAL_CACHE
|
|
declare -gA GLOBAL_CACHE
|
|
|
|
callTemplateFunction "${filename}" "${suffix}" "${function}"
|
|
setArrayAsGlobal GLOBAL_CACHE $return_global_var
|
|
|
|
if [ ! ${#GLOBAL_CACHE[@]} -eq 0 ]; then
|
|
debug "Smart files located for: '${filename##*/}' (suffix: ${suffix}):"
|
|
for filename in "${GLOBAL_CACHE[@]}"; do
|
|
debug "${filename}"
|
|
done
|
|
fi
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Executes any additional optional configuration steps if the configuration
|
|
# scripts exist
|
|
#
|
|
# Will find all scripts with
|
|
# Example:
|
|
# filename = 04_install_qubes.sh
|
|
# suffix = post
|
|
#
|
|
# Will look for a file name 04_install_qubes_post in:
|
|
# the $SCRIPTSDIR; beside original
|
|
# the $SCRIPTSDIR/$DIST (wheezy) directory
|
|
# any included template module directories ($SCRIPTSDIR/gnome)
|
|
#
|
|
# All matches are executed
|
|
# ------------------------------------------------------------------------------
|
|
buildStep() {
|
|
local filename="$1"
|
|
local suffix="$2"
|
|
unset build_step_files
|
|
|
|
info "Locating buildStep files: ${filename##*/} suffix: ${suffix}"
|
|
getFileLocations "build_step_files" "${filename}" "${suffix}"
|
|
|
|
for script in "${build_step_files[@]}"; do
|
|
if [ -e "${script}" ]; then
|
|
# Test module expects raw output back only used to asser test results
|
|
if [[ -n ${TEST} ]]; then
|
|
echo "${script}"
|
|
else
|
|
output "${bold}${under}INFO: Currently running script: ${script}${reset}"
|
|
fi
|
|
|
|
# Execute $script
|
|
"${script}"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Copy extra file tree to ${INSTALLDIR}
|
|
# TODO: Allow copy per step (04_install_qubes.sh-files)
|
|
#
|
|
# To set file permissions is a PITA since git won't save them and will
|
|
# complain heavily if they are set to root only read, so this is the procdure:
|
|
#
|
|
# 1. Change to the directory that you want to have file permissions retained
|
|
# 2. Change all the file permissions / ownership as you want
|
|
# 3. Change back to the root of the exta directory (IE: extra-qubes-files)
|
|
# 4. Manually restore facl's: setfacl --restore=.facl
|
|
# 5. Manually create facl backup used after copying: getfacl -R . > .facl
|
|
# 6. If git complains; reset file ownership back to user. The .facl file stored
|
|
# the file permissions and will be used to reset the file permissions after
|
|
# they get copied over to ${INSTALLDIR}
|
|
# NOTE: Don't forget to redo this process if you add -OR- remove files
|
|
# ------------------------------------------------------------------------------
|
|
copyTree() {
|
|
local dir="$1"
|
|
local source_dir="$2"
|
|
local target_dir="$3"
|
|
local function="copyTreeExec"
|
|
|
|
if [ -z "${source_dir}" ]; then
|
|
splitPath "${0}" path_parts
|
|
if [ -d "${path_parts[dir]}/${dir}" ]; then
|
|
copyTreeExec "${path_parts[dir]}" "${dir}" "" ""
|
|
else
|
|
callTemplateFunction "" "${dir}" "${function}"
|
|
fi
|
|
else
|
|
copyTreeExec "${source_dir}" "${dir}" "" "${target_dir}"
|
|
fi
|
|
}
|
|
|
|
# $0 is module that sourced vars.sh
|
|
output "${bold}${under}INFO: Currently running script: ${0}${reset}"
|