aboutsummaryrefslogtreecommitdiff
path: root/lib/util.bash
diff options
context:
space:
mode:
authorPatrick Spek <p.spek@tyil.nl>2022-04-15 16:32:43 +0200
committerPatrick Spek <p.spek@tyil.nl>2022-04-15 16:32:43 +0200
commitd8a2f732b300cdbb892e0878fe87dbb7a0ef6d03 (patch)
treed364845506af8f3080c79df9a91bb3e32cc4b4d8 /lib/util.bash
Initial commit
Diffstat (limited to 'lib/util.bash')
-rw-r--r--lib/util.bash365
1 files changed, 365 insertions, 0 deletions
diff --git a/lib/util.bash b/lib/util.bash
new file mode 100644
index 0000000..2c10ef4
--- /dev/null
+++ b/lib/util.bash
@@ -0,0 +1,365 @@
+#!/usr/bin/env bash
+
+# Change the working directory. In usage, this is the same as using cd,
+# however, it will make additional checks to ensure everything is going fine.
+chgdir() {
+ debug "tyilnet/chgdir" "Changing workdir to $1"
+ cd -- "$1" || die "Failed to change directory to $1"
+}
+
+# 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_for() {
+ local default
+ local file
+ local files
+ local host
+ local value
+ local key
+
+ host=$1 ; shift
+ key=$1 ; shift
+ default=$1 ; shift
+
+ files=(
+ "$BASHTARD_ETCDIR/hosts.d/$host"
+ "$BASHTARD_ETCDIR/os.d/${BASHTARD_PLATFORM[key]}"
+ "$BASHTARD_ETCDIR/defaults"
+ "$BASHTARD_BASEDIR/etc/os.d/${BASHTARD_PLATFORM[key]}"
+ "$BASHTARD_BASEDIR/etc/defaults"
+ )
+
+ # Check configuration files
+ for file in "${files[@]}"
+ do
+ [[ ! -f $file ]] && continue
+
+ value="$(awk -F= '$1 == "'"$key"'" { print $NF }' "$file")"
+
+ if [[ -n $value ]]
+ then
+ debug "tyilnet/config_for" "Found $key=$value in $file"
+
+ printf "%s" "$value"
+ return
+ fi
+ done
+
+ # Return default value
+ if [[ -n $default ]]
+ then
+ printf "%s" "$default"
+ return
+ fi
+
+ # Error
+ alert "tyilnet/config_for" "No configuration value for $key"
+}
+
+# Create a datetime stamp. This is a wrapper around the date utility, ensuring
+# that the date being formatted is always in UTC and respect SOURCE_DATE_EPOCH,
+# if it is set.
+datetime() {
+ local date_opts
+
+ # Apply SOURCE_DATE_EPOCH as the date to base off of.
+ if [[ $SOURCE_DATE_EPOCH ]]
+ then
+ date_opts+=("-d@$SOURCE_DATE_EPOCH")
+ date_opts+=("-u")
+ fi
+
+ date "${date_opts[@]}" +"${1:-%FT%T}"
+}
+
+# Log a message as error, and exit the program. This is intended for serious
+# issues that prevent the script from running correctly. The exit code can be
+# specified with -i, or will default to 1.
+die() {
+ local OPTIND
+ local code
+
+ while getopts ":i:" opt
+ do
+ case "$opt" in
+ i) code=$OPTARG ;;
+ *) alert "tyilnet/die" "Unused argument specified: $opt" ;;
+ esac
+ done
+
+ shift $(( OPTIND -1 ))
+
+ alert "$@"
+ exit "${code:-1}"
+}
+
+# Fetch a file from an URL. Using this function introduces a dependency on curl.
+fetch_http() {
+ local OPTIND
+ local buffer
+
+ while getopts ":o:" opt
+ do
+ case "$opt" in
+ o) buffer=$OPTARG ;;
+ *) alert "tyilnet/fetch_http" "Unused argument specified: $opt" ;;
+ esac
+ done
+
+ shift $(( OPTIND -1 ))
+
+ [[ -z $buffer ]] && buffer="$(tmpfile)"
+
+ notice "tyilnet/fetch_http" "Downloading $1 to $buffer"
+
+ for util in curl wget
+ do
+ command -v "$util" > /dev/null || continue
+ "fetch_http_$util" "$1" "$buffer" || continue
+ local exit_code=$?
+
+ printf "%s" "$buffer"
+ return $exit_code
+ done
+
+ die "tyilnet/fetch_http" "Unable to download file over HTTP!"
+}
+
+fetch_http_curl() {
+ curl -Ls "$1" > "$2"
+}
+
+fetch_http_wget() {
+ wget --quiet --output-document "$2" "$1"
+}
+
+# Check if the first argument given appears in the list of all following
+# arguments.
+in_args() {
+ local needle="$1"
+ shift
+
+ for arg in "$@"
+ do
+ [[ $needle == "$arg" ]] && return 0
+ done
+
+ return 1
+}
+
+# Join a list of arguments into a single string. By default, this will join
+# using a ",", but you can set a different character using -c. Note that this
+# only joins with a single character, not a string of characters.
+join_args() {
+ local OPTIND
+ local IFS=","
+
+ while getopts ":c:" opt
+ do
+ case "$opt" in
+ c) IFS="$OPTARG" ;;
+ *) warn "tyilnet/join_args" "Unused opt specified: $opt" ;;
+ esac
+ done
+
+ shift $(( OPTIND - 1))
+
+ printf "%s" "$*"
+}
+
+# OS independent package management
+pkg() {
+ local system="tyilnet/pkg"
+
+ local action=$1 ; shift
+ local app="$(config "pkg.$1" "$(config "app.$1")")" ; shift
+
+ local type
+
+ if [[ -z $app ]]
+ then
+ crit "$system" "No package name for $app"
+ return 1
+ fi
+
+ if [[ "$(type -t pkg_$action)" != "function" ]]
+ then
+ crit "$system" "Invalid package manager action $action"
+ return 1
+ fi
+
+ "pkg_$action" "$app"
+}
+
+pkg_install() {
+ local system="tyilnet/pkg/install"
+
+ local app=$1 ; shift
+
+ case "${BASHTARD_PLATFORM[key]}" in
+ freebsd) set -- /usr/sbin/pkg install -y "$app" ;;
+ linux-debian*) set -- apt install -y "$app" ;;
+ linux-gentoo) set -- emerge --ask=n "$app" ;;
+ *)
+ crit "$system" "No package manager configured for ${BASHTARD_PLATFORM[key]}"
+ return 1
+ ;;
+ esac
+
+ notice "$system" "$*"
+ $@
+}
+
+# OS independent service management.
+svc() {
+ local system="tyilnet/svc"
+
+ local service
+ local action
+
+ service="$(config svc.$1)" ; shift
+ action=$1 ; shift
+
+ if [[ -z $service ]]
+ then
+ crit "$system" "No service name for $service"
+ return 1
+ fi
+
+ if [[ "$(type -t svc_$action)" != "function" ]]
+ then
+ crit "$system" "Invalid service manager action $action"
+ return 1
+ fi
+
+ "svc_$action" "$service"
+}
+
+svc_enable() {
+ local system="tyilnet/svc/enable"
+
+ local service=$1
+
+ case "${BASHTARD_PLATFORM[key]}" in
+ linux-gentoo) set -- /sbin/rc-update add "$service" ;;
+ linux-*) set -- systemctl enable "$service" ;;
+ *)
+ crit "$system" "No service manager configured for ${BASHTARD_PLATFORM[key]}"
+ return 1
+ esac
+
+ notice "$system" "$*"
+ $@
+}
+
+svc_restart() {
+ local system="tyilnet/svc/restart"
+
+ local service=$1
+
+ case "${BASHTARD_PLATFORM[key]}" in
+ freebsd) set -- service "$service" restart ;;
+ linux-gentoo) set -- /sbin/rc-service "$service" restart ;;
+ linux-*) set -- systemctl restart "$service" ;;
+ *)
+ crit "$system" "No service manager configured for ${BASHTARD_PLATFORM[key]}"
+ return 1
+ esac
+
+ notice "$system" "$*"
+ $@
+}
+
+svc_start() {
+ local system="tyilnet/svc/start"
+
+ local service=$1
+
+ case "${BASHTARD_PLATFORM[key]}" in
+ freebsd) set -- service "$service" start ;;
+ linux-gentoo) set -- /sbin/rc-service "$service" start ;;
+ linux-*) set -- systemctl start "$service" ;;
+ *)
+ crit "$system" "No service manager configured for ${BASHTARD_PLATFORM[key]}"
+ return 1
+ esac
+
+ notice "$system" "$*"
+ $@
+}
+
+template()
+{
+ local file="$BASEDIR/etc/templates/$1" ; shift
+ local sedfile="$(tmpfile)"
+
+ if [[ ! -f $file ]]
+ then
+ crit "tyilnet/template" "Tried to render template from $file, but it doesn't exist"
+ return
+ fi
+
+ for kv in "$@"
+ do
+ debug "tyilnet/template" "Adding $kv to sedfile at $sedfile"
+
+ key="$(awk -F= '{ print $1 }' <<< $kv)"
+
+ if [[ -z "$key" ]]
+ then
+ crit "tyilnet/template" "Empty key in '$kv' while rendering $file?"
+ fi
+
+ value="$(awk -F= '{ print $NF }' <<< $kv)"
+
+ if [[ -z "$value" ]]
+ then
+ crit "tyilnet/template" "Empty key in '$kv' while rendering $file?"
+ fi
+
+ printf 's@${%s}@%s@g\n' "$key" "$value" >> "$sedfile"
+ done
+
+ sed -f "$sedfile" "$file"
+}
+
+# Create a temporary directory. Similar to tempfile, but you'll get a directory
+# instead.
+tmpdir() {
+ local dir
+
+ dir="$(mktemp -d)"
+
+ # Ensure the file was created succesfully
+ if [[ ! -d "$dir" ]]
+ then
+ die "tyilnet/tmpdir" "Failed to create a temporary directory at $dir"
+ fi
+
+ debug "tyilnet/tmpdir" "Temporary file created at $dir"
+
+ printf "%s" "$dir"
+}
+
+# Create a temporary file. In usage, this is no different from mktemp itself,
+# however, it will apply additional checks to ensure everything is going
+# correctly, and the files will be cleaned up automatically at the end.
+tmpfile() {
+ local file
+
+ file="$(mktemp)"
+
+ # Ensure the file was created succesfully
+ if [[ ! -f "$file" ]]
+ then
+ die "tyilnet/tmpfile" "Failed to create a temporary file at $file"
+ fi
+
+ debug "tyilnet/tmpfile" "Temporary file created at $file"
+
+ printf "%s" "$file"
+}