#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2023 Patrick Spek
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# Read a particular value from a key/value configuration file. Using this
# function introduces a dependency on awk.
config() {
config_for "${BASHTARD_PLATFORM[fqdn]}" "$@"
}
config_subkeys() {
config_subkeys_for "${BASHTARD_PLATFORM[fqdn]}" "$@"
}
config_for() {
local host=$1 ; shift
local key=$1 ; shift
# Use a variable definition test to define default, in order to ensure
# it is _not_ defined if no argument for it was passed, but _is_
# defined even if an empty string was passed.
test -v 1 && { local default=$1 ; shift ; }
local default
local file
local files
files=(
"$BASHTARD_ETCDIR/secrets"
"$BASHTARD_ETCDIR/hosts.d/$host"
"$BASHTARD_ETCDIR/os.d/${BASHTARD_PLATFORM[key]}"
"$BASHTARD_ETCDIR/defaults"
)
if [[ -n "$BASHTARD_PLAYBOOK" ]]
then
debug "bashtard/config_for" "BASHTARD_PLAYBOOK=$BASHTARD_PLAYBOOK, adding etc entries"
files+=(
"$(playbook_path "base")/etc/os.d/${BASHTARD_PLATFORM[key]}"
"$(playbook_path "base")/etc/defaults"
)
fi
files+=(
"$BASHTARD_SHAREDIR/os.d/${BASHTARD_PLATFORM[key]}"
"$BASHTARD_SHAREDIR/defaults"
)
# Check configuration files
for file in "${files[@]}"
do
debug "bashtard/config_for" "Checking for '$key' in '$file'"
[[ ! -f $file ]] && continue
# Check if the lookup is a reference variable, defined by using
# &= instead of just a single = as seperator. If this exists,
# do a new config_for lookup, this time using the value as the
# key for the new lookup.
value="$(awk -F= '$1 == "'"$key"'&" { print $0 }' "$file" | cut -d'=' -f 2-)"
if [[ -n $value ]]
then
debug "bashtard/config_for" "Found reference for $key to $value in $file"
printf "%s" "$(config_for "$host" "$value")"
return
fi
# Use awk to find the right line, then use cut to get the
# actual value. Cutting it out with awk _is_ possible, but
# comes with whitespace issues or having to deal with values
# containing the seperator (=), so using cut is much simpler
# and easier to understand.
value="$(awk -F= '$1 == "'"$key"'" { print $0 }' "$file" | cut -d'=' -f 2-)"
if [[ -n $value ]]
then
debug "bashtard/config_for" "Found $key=$value in $file"
printf "%s" "$value"
return
fi
done
# Return default value, if one has been defined
if test -v default
then
printf "%s" "$default"
return
fi
# Error
alert "bashtard/config_for" "No configuration value for $key"
}
config_subkeys_for() {
local host=$1 ; shift
local key=$1 ; shift
local file
local files
local results
files=(
"$BASHTARD_ETCDIR/secrets"
"$BASHTARD_ETCDIR/hosts.d/$host"
"$BASHTARD_ETCDIR/os.d/${BASHTARD_PLATFORM[key]}"
"$BASHTARD_ETCDIR/defaults"
)
if [[ -n "$BASHTARD_PLAYBOOK" ]]
then
debug "bashtard/config_for" "BASHTARD_PLAYBOOK=$BASHTARD_PLAYBOOK, adding etc entries"
files+=(
"$BASHTARD_ETCDIR/playbooks.d/$BASHTARD_PLAYBOOK/etc/os.d/${BASHTARD_PLATFORM[key]}"
"$BASHTARD_ETCDIR/playbooks.d/$BASHTARD_PLAYBOOK/etc/defaults"
)
fi
files+=(
"$BASHTARD_SHAREDIR/os.d/${BASHTARD_PLATFORM[key]}"
"$BASHTARD_SHAREDIR/defaults"
)
# Check configuration files
for file in "${files[@]}"
do
debug "bashtard/config_subkeys" "Checking for subkeys of '$key' in '$file'"
[[ ! -f $file ]] && continue
while read -r result
do
local subkey
subkey="$(awk -F. '{ print $1 }' <<< "${result#"$key."}")"
# Remove trailing ampersand if it exists
[[ "${subkey: -1}" == "&" ]] && subkey="${subkey:0:-1}"
debug "bashtard/config_subkeys" "Found '$subkey' as subkey of '$key' through '$result'"
results+=("$subkey")
done < <(grep "^$key\." "$file" | awk -F= '{ print $1 }')
done
# Return unique results
for result in "${results[@]}"
do
printf "%s\n" "$result"
done | sort -u
}