From d8a2f732b300cdbb892e0878fe87dbb7a0ef6d03 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Fri, 15 Apr 2022 16:32:43 +0200 Subject: Initial commit --- lib/util.bash | 365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 lib/util.bash (limited to 'lib/util.bash') 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" +} -- cgit v1.1