#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2023 Patrick Spek
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# shellcheck source=lib/util.bash
source "$(dirname "${BASH_SOURCE[0]}")/util.bash"
# shellcheck source=lib/logging.bash
source "$(dirname "${BASH_SOURCE[0]}")/logging.bash"
main() {
debug "$BASHTARD_NAME/main" "Running from $BASHTARD_LIBDIR"
debug "$BASHTARD_NAME/main" "Configuration dir is at $BASHTARD_ETCDIR"
debug "$BASHTARD_NAME/main" "> $0 $*"
[[ -z $1 ]] && usage && exit 2
export BASHTARD_COMMAND="$1" ; shift
debug "$BASHTARD_NAME/main" "Handling subcommand '$BASHTARD_COMMAND'"
subcommand_src="$BASHTARD_LIBDIR/subcommands/$BASHTARD_COMMAND.bash"
debug "$BASHTARD_NAME/main" "Checking $subcommand_src"
if [[ ! -f $subcommand_src ]]
then
debug "$BASHTARD_NAME/main" "No script found to handle action, showing usage"
usage
exit 2
fi
# Declare some global variables
declare -A BASHTARD_PLATFORM
declare -A BASHTARD_PLAYBOOK_VARS
# Figure out system details
debug "$BASHTARD_NAME/main" "Discovering system information"
discover_system
# Export BASHTARD_ variables
export BASHTARD_PLATFORM
export BASHTARD_PLAYBOOK_VARS
# Source the file defining the subcommand.
debug "$BASHTARD_NAME/main" "Sourcing $subcommand_src"
source "$subcommand_src"
# Maintain our own tempdir
export TMPDIR="$BASEDIR/tmp/$RANDOM"
mkdir -p -- "$TMPDIR"
debug "$BASHTARD_NAME/main" "\$TMPDIR set to $TMPDIR"
# Actually perform the subcommand
debug "$BASHTARD_NAME/main" "Running subcommand '$BASHTARD_COMMAND'"
subcommand "$@"
local subcommand_exit=$?
# Clean up if necessary
if [[ -z $BASHTARD_MESSY ]]
then
debug "$BASHTARD_NAME/main" "Cleaning up tempfiles at $TMPDIR"
rm -rf -- "$TMPDIR"
fi
## Use the subcommand's exit code
exit $subcommand_exit
}
usage() {
local playbooks
local playbook_length
local playbook_longest=0
cat <
$BASHTARD_NAME del
$BASHTARD_NAME diff
$BASHTARD_NAME init [repository]
$BASHTARD_NAME pkg
$BASHTARD_NAME pull
$BASHTARD_NAME ssh
$BASHTARD_NAME sync [playbook]
$BASHTARD_NAME sysinfo
$BASHTARD_NAME top
$BASHTARD_NAME var [-p ]
$BASHTARD_NAME var [-s]
Perform maintenance on your infra.
Commands:
add Add a configuration playbook to this machine.
del Remove a configuration playbook from this machine.
diff Show a diff of all uncommitted changes.
init Initialize the $BASHTARD_NAME configuration system.
pkg Interface into the pkg abstraction used by $BASHTARD_NAME.
pull Pull the latest changes through git.
ssh Run a given command on all known hosts.
sync Pull latest changes through git, and synchronize all added
playbooks.
sysinfo Show gathered information about this system.
top Show resource information about all known hosts.
var Show or set the value of a given configuration key.
EOF
if [[ ! -d "$BASHTARD_ETCDIR/playbooks.d" ]]
then
return 0
fi
printf "\nPlaybooks:\n"
# Find all playbooks
mapfile -t playbooks < <(find \
"$BASHTARD_ETCDIR/playbooks.d"/* \
-maxdepth 0 \
-type d \
)
# Find longest playbook name
for playbook in "${playbooks[@]}"
do
playbook_length="$(printf "%s" "$(basename "$playbook")" | wc -c)"
if (( playbook_longest < playbook_length ))
then
playbook_longest=$playbook_length
fi
done
# Render playbook descriptions
for playbook in "${playbooks[@]}"
do
printf "\t%-${playbook_longest}s %s\n" \
"$(basename "$playbook")" \
"$(cat "$playbook/description.txt")"
done
}
# Discover information about the system. If any bugs are reported and you want
# more information about the system the user is running on, additional checks
# can be added here, and the user will simply have to include the output of the
# sysinfo command in their message to you.
discover_system() {
BASHTARD_PLATFORM["os"]="$(discover_system_os)"
BASHTARD_PLATFORM["arch"]="$(discover_system_arch)"
BASHTARD_PLATFORM["version"]="$(discover_system_version)"
BASHTARD_PLATFORM["term"]="$TERM"
BASHTARD_PLATFORM["fqdn"]="$(hostname -f)"
BASHTARD_PLATFORM["init"]="$(discover_system_init)"
# When on a Linux-using OS, check for the specific distribution in use.
if [[ ${BASHTARD_PLATFORM[os]} == *"linux"* ]]
then
BASHTARD_PLATFORM["distro"]="$(discover_system_distro)"
fi
BASHTARD_PLATFORM[key]="$(discover_system_key)"
}
discover_system_arch() {
uname -m
}
discover_system_distro() {
if [[ -f /etc/os-release ]]
then
(
source /etc/os-release
printf "%s" "$NAME" \
| awk '{print tolower($0)}' \
| sed 's@[/+ ]@_@g'
)
return
fi
crit "No /etc/os-release found. Are you sure you're on a sane GNU+Linux distribution?"
if command -v pacman > /dev/null
then
warn "$BASHTARD_NAME/main" "Found pacman, assuming Archlinux as distro."
printf "%s" "archlinux"
return
fi
}
discover_system_init() {
local sbin_init
# Differing init systems seems to only be a thing on GNU+Linux
if [[ ${BASHTARD_PLATFORM["os"]} != "linux" ]]
then
return
fi
sbin_init="$(readlink -f "/sbin/init" | awk -F/ '{ print $NF }')"
# If /sbin/init is a symlink, it will point us towards the right thing
if [[ $sbin_init != "init" ]]
then
printf "%s" "$sbin_init"
return
fi
# Otherwise we try out /etc/inittab
if [[ -f "/etc/inittab" ]]
then
awk -F: '/^rc:/ { print $4 }' /etc/inittab \
| awk '{ print $1 }' \
| awk -F/ '{ print $NF }'
return
fi
# If we can't figure it out, show a warning
warn "$BASHTARD_NAME/util" "Can't figure out system init"
}
discover_system_key() {
local key
# shellcheck disable=SC2031
key+="${BASHTARD_PLATFORM[os]}"
if [[ ${BASHTARD_PLATFORM[distro]} ]]
then
key+="-${BASHTARD_PLATFORM[distro]}"
fi
printf "%s" "$key"
}
discover_system_os() {
if command -v uname > /dev/null
then
printf "%s" "$(uname -s | awk '{print tolower($0)}' | sed 's@[/+ ]@_@g')"
return
fi
}
discover_system_version() {
printf "%s" "$(uname -r | awk '{print tolower($0)}')"
}
main "$@"