Zernit/src/RXT0112-1/downstream-classes/zeres-0/bash/UNLEASH.sh

337 lines
14 KiB
Bash

#!/bin/sh
# All rights reserved by Jacob Hrbek <kreyren@rixotstudio.cz> in 04/2020 (Prepared for four freedom respecting license)
# Peer-reviewed by: <YOUR_NAME> <YOUR_EMAIL> in <DATE+TIME+TIMEZONE>
# shellcheck shell=sh
###! Base bashrc for zernit's bash backend
###! Requires:
# FIXME: Implement logic for this
###! - Command 'uname' to identify the kernel
# FIXME: Implement logic for this
###! - Command 'lsb_release' or file /etc/os-release to identify distribution and release on linux
###! Exit codes:
###! - FIXME-DOCS(Krey): Defined in die()
###! Platforms:
###! - [ ] Linux
###! - [ ] Debian
###! - [ ] Ubuntu
###! - [ ] Fedora
###! - [ ] NixOS
###! - [ ] Archlinux
###! - [ ] Alpine
###! - [ ] FreeBSD
###! - [ ] Darwin
###! - MacOS
###! - [ ] Redox
###! - [ ] ReactOS
###! - [ ] Windows
###! - [ ] Windows xp
###! - [ ] Windows 7
###! - [ ] Windows 8
###! - [ ] Windows 8.1
###! - [ ] Windows 10
###! - [ ] Windows/Cygwin
###! - [ ] Windows 10
###! Code Quality:
###! - Replace all external commands alike 'uname' with variable to be replaced in command overrides
###! - Everything has to be sanitized
###! - Has to be tested in docker and vagrant
###! - echo() is non-standard on XNU/Darwin -> Use printf
###! - Apply styles to allow translate of the output
###! Resources:
###! - https://pkgs.org | To search Linux distros for files and package informations
# Maintainer info
UPSTREAM="https://github.com/RXT0112/Zernit"
UPSTREAM_NAME="RiXotStudio"
UPSTREAM_EMAIL="rxt0112@rixotstudio.cz"
MAINTAINER_EMAIL="kreyren@rixotstudio.cz"
MAINTAINER_NICKNAME="kreyren"
MAINTAINER_NAME="Jacob"
MAINTAINER_SURNAME="Hrbek"
# FIXME-SUGGESTION: _=${var:="some text"} is less verbose and less error prone than [ -z "$var" ] && var="some text"
# Command overrides
## These may be required on some systems
[ -z "$PRINTF" ] && PRINTF="printf"
[ -z "$WGET" ] && WGET="wget"
[ -z "$CURL" ] && CURL="curl"
[ -z "$ARIA2C" ] && ARIA2C="aria2c"
[ -z "$CHMOD" ] && CHMOD="chmod"
[ -z "$UNAME" ] && UNAME="uname"
[ -z "$TR" ] && TR="tr"
[ -z "$SED" ] && SED="sed"
[ -z "$GREP" ] && GREP="grep"
# Customization of the output
## efixme
[ -z "$EFIXME_FORMAT_STRING" ] && EFIXME_FORMAT_STRING="FIXME: %s\n"
[ -z "$EFIXME_FORMAT_STRING_LOG" ] && EFIXME_FORMAT_STRING_LOG="${logPrefix}FIXME: %s\n"
[ -z "$EFIXME_FORMAT_STRING_DEBUG" ] && EFIXME_FORMAT_STRING_DEBUG="FIXME($myName:$LINENO): %s\n"
[ -z "$EFIXME_FORMAT_STRING_DEBUG_LOG" ] && EFIXME_FORMAT_STRING_DEBUG_LOG="${logPrefix}FIXME($myName:$LINENO): %s\n"
## eerror
[ -z "$EERROR_FORMAT_STRING" ] && EERROR_FORMAT_STRING="ERROR: %s\n"
[ -z "$EERROR_FORMAT_STRING_LOG" ] && EERROR_FORMAT_STRING_LOG="${logPrefix}ERROR: %s\n"
[ -z "$EERROR_FORMAT_STRING_DEBUG" ] && EERROR_FORMAT_STRING_DEBUG="ERROR($myName:$0): %s\n"
[ -z "$EERROR_FORMAT_STRING_DEBUG_LOG" ] && EERROR_FORMAT_STRING_DEBUG_LOG="${logPrefix}ERROR($myName:$0): %s\n"
## edebug
[ -z "$EERROR_FORMAT_STRING" ] && EERROR_FORMAT_STRING="ERROR: %s\n"
[ -z "$EERROR_FORMAT_STRING_LOG" ] && EERROR_FORMAT_STRING_LOG="${logPrefix}ERROR: %s\n"
[ -z "$EERROR_FORMAT_STRING_DEBUG" ] && EERROR_FORMAT_STRING_DEBUG="ERROR($myName:$0): %s\n"
[ -z "$EERROR_FORMAT_STRING_DEBUG_LOG" ] && EERROR_FORMAT_STRING_DEBUG_LOG="${logPrefix}ERROR($myName:$0): %s\n"
## einfo
[ -z "$EINFO_FORMAT_STRING" ] && EINFO_FORMAT_STRING="INFO: %s\n"
[ -z "$EINFO_FORMAT_STRING_LOG" ] && EINFO_FORMAT_STRING_LOG="${logPrefix}INFO: %s\n"
[ -z "$EINFO_FORMAT_STRING_DEBUG" ] && EINFO_FORMAT_STRING_DEBUG="INFO($myName:$0): %s\n"
[ -z "$EINFO_FORMAT_STRING_DEBUG_LOG" ] && EINFO_FORMAT_STRING_DEBUG_LOG="${logPrefix}INFO($myName:$0): %s\n"
## die
### Generic
[ -z "$DIE_FORMAT_STRING" ] && DIE_FORMAT_STRING="FATAL: %s in script '$myName' located at '$0'\\n"
[ -z "$DIE_FORMAT_STRING_LOG" ] && DIE_FORMAT_STRING_LOG="${logPath}FATAL: %s in script '$myName' located at '$0'\\n"
[ -z "$DIE_FORMAT_STRING_DEBUG" ] && DIE_FORMAT_STRING_DEBUG="FATAL($myName:$1): %s\n"
[ -z "$DIE_FORMAT_STRING_DEBUG_LOG" ] && DIE_FORMAT_STRING_DEBUG_LOG="${logPrefix}FATAL($myName:$1): %s\\n"
### Success trap
[ -z "$DIE_FORMAT_STRING_SUCCESS" ] && DIE_FORMAT_STRING_SUCCESS="SUCCESS: Script '$myName' located at '$0' finished successfully\\n"
[ -z "$DIE_FORMAT_STRING_LOG" ] && DIE_FORMAT_STRING_LOG="${logPath}$DIE_FORMAT_STRING_SUCCESS"
[ -z "$DIE_FORMAT_STRING_DEBUG" ] && DIE_FORMAT_STRING_DEBUG="SUCCESS($myName:$1): Script '$myName' located at '$0' finished successfully\\n"
[ -z "$DIE_FORMAT_STRING_DEBUG_LOG" ] && DIE_FORMAT_STRING_DEBUG_LOG="${logPrefix}$DIE_FORMAT_STRING_DEBUG_LOG"
### Syntax error (syntaxerr) trap
[ -z "$DIE_FORMAT_STRING_SYNTAXERR" ] && DIE_FORMAT_STRING_SYNTAXERR="SyntaxErr: Invalid argument(s) '$0' '$1' '$2' '$3' '$4' has been provided to $myName\\n"
[ -z "$DIE_FORMAT_STRING_SYNTAXERR_LOG" ] && DIE_FORMAT_STRING_LOG="${logPath}$DIE_FORMAT_STRING_SUCCESS"
[ -z "$DIE_FORMAT_STRING_SYNTAXERR_DEBUG" ] && DIE_FORMAT_STRING_DEBUG="SyntaxErr($myName:$1): Invalid argument(s) '$0' '$1' '$2' '$3' '$4' has been provided to $myName\\n"
[ -z "$DIE_FORMAT_STRING_SYNTAXERR_DEBUG_LOG" ] && DIE_FORMAT_STRING_DEBUG_LOG="${logPrefix}$DIE_FORMAT_STRING_DEBUG_LOG"
### Fixme trap
[ -z "$DIE_FORMAT_STRING_FIXME" ] && DIE_FORMAT_STRING_FIXME="FATAL: %s in script '$myName' located at '$0', fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_LOG" ] && DIE_FORMAT_STRING_FIXME_LOG="${logPrefix}FATAL: %s, fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_DEBUG" ] && DIE_FORMAT_STRING_FIXME_DEBUG="FATAL($myName:$1): %s, fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_DEBUG_LOG" ] && DIE_FORMAT_STRING_FIXME_DEBUG_LOG="${logPrefix}FATAL($myName:$1): %s, fixme?\\n"
### Bug Trap
[ -z "$DIE_FORMAT_STRING_BUG" ] && DIE_FORMAT_STRING_BUG="BUG: Unexpected happend while processing %s in script '$myName' located at '$0'\\n\\nIf you think that this is a bug, the report it to $UPSTREAM to @$MAINTAINER_NICKNAME with output from $logPath for relevant runtime"
[ -z "$DIE_FORMAT_STRING_BUG_LOG" ] && DIE_FORMAT_STRING_BUG_LOG="${logPrefix}$DIE_FORMAT_STRING_BUG"
[ -z "$DIE_FORMAT_STRING_BUG_DEBUG" ] && DIE_FORMAT_STRING_BUG_DEBUG="BUG:($myName:$1): ${DIE_FORMAT_STRING_BUG%%BUG:}"
[ -z "$DIE_FORMAT_STRING_BUG_DEBUG_LOG" ] && DIE_FORMAT_STRING_BUG_DEBUG_LOG="${logPrefix}$DIE_FORMAT_STRING_BUG_DEBUG"
### Fixme trap
[ -z "$DIE_FORMAT_STRING_FIXME" ] && DIE_FORMAT_STRING_FIXME="FATAL: %s in script '$myName' located at '$0', fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_LOG" ] && DIE_FORMAT_STRING_FIXME_LOG="${logPrefix}FATAL: %s, fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_DEBUG" ] && DIE_FORMAT_STRING_FIXME_DEBUG="FATAL($myName:$1): %s, fixme?\n"
[ -z "$DIE_FORMAT_STRING_FIXME_DEBUG_LOG" ] && DIE_FORMAT_STRING_FIXME_DEBUG_LOG="${logPrefix}FATAL($myName:$1): %s, fixme?\\n"
### Unexpected trap
[ -z "$DIE_FORMAT_STRING_UNEXPECTED" ] && DIE_FORMAT_STRING_UNEXPECTED="FATAL: Unexpected happend while %s in $myName located at $0\\n"
[ -z "$DIE_FORMAT_STRING_UNEXPECTED_LOG" ] && DIE_FORMAT_STRING_UNEXPECTED_LOG="${logPrefix}FATAL: Unexpected happend while %s\\n"
[ -z "$DIE_FORMAT_STRING_UNEXPECTED_DEBUG" ] && DIE_FORMAT_STRING_UNEXPECTED_DEBUG="FATAL($myName:$1): Unexpected happend while %s in $myName located at $0\\n"
[ -z "$DIE_FORMAT_STRING_UNEXPECTED_DEBUG_LOG" ] && DIE_FORMAT_STRING_UNEXPECTED_DEBUG="${logPrefix}FATAL($myName:$1): Unexpected happend while %s\\n"
# elog
[ -z "$ELOG_FORMAT_STRING_DEBUG_LOG" ] && ELOG_FORMAT_STRING_DEBUG_LOG="${logPrefix}LOG: %s\\n"
# ebench
[ -z "$EBENCH_FORMAT_STRING_START" ] && EBENCH_FORMAT_STRING_START="BENCHMARK: Starting benchmark for action %s\n"
[ -z "$EBENCH_FORMAT_STRING_RESULT" ] && EBENCH_FORMAT_STRING_RESULT="BENCHMARK: Action %s took $SECONDS seconds\n"
# invoke_privileged
[ -z "$INVOKE_PRIVILEGED_FORMAT_STRING_QUESTION" ] && INVOKE_PRIVILEGED_FORMAT_STRING_QUESTION="### PRIVILEGED ACCESS REQUEST ###\n\n\s\n"
# Exit on anything unexpected
set -e
# NOTICE(Krey): By default busybox outputs a full path in '$0' this is used to strip it
myName="${0##*/}"
# Used to prefix logs with timestemps, uses ISO 8601 by default
logPrefix="[ $(date -u +"%Y-%m-%dT%H:%M:%SZ") ] "
# Path to which we will save logs
# NOTICE(Krey): To avoid storing file '$HOME/.some-name.sh.log' we are stripping the '.sh' here
# FIXME-QA: Make sure the the directory path is present or this fails
# FIXME-COMPAT: Make sure this works on Windows and Darwin
logPath="${XDG_DATA_HOME:-$HOME/.local/share}/${myName%%.sh}.log"
# inicialize the script in logs
# FIXME: Allow end-users to customize this
"$PRINTF" '%s\n' "Started $myName on $("$UNAME" -s) at $(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$logPath"
# DNM: Specify the path to die()
. path/to/die
# DNM: Specify the path
. path/to/einfo
. path/to/ewarn
. path/to/efixme
efixme() { funcname=efixme
if [ "$IGNORE_FIXME" = 1 ]; then
edebug fixme "Fixme message for '$2' disabled"
elif [ "$IGNORE_FIXME" = 0 ] || [ -z "$IGNORE_FIXME" ]; then
if [ "$DEBUG" = 0 ] || [ -z "$DEBUG" ]; then
"$PRINTF" "$EFIXME_FORMAT_STRING" "$2"
"$PRINTF" "$EFIXME_FORMAT_STRING" "$2" >> "$logPath"
unset funcname
return 0
elif [ "$DEBUG" = 1 ]; then
"$PRINTF" "$EFIXME_FORMAT_STRING_DEBUG" "$2"
"$PRINTF" "$EFIXME_FORMAT_STRING_DEBUG_LOG" "$2" >> "$logPath"
unset funcname
return 0
else
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) die 255 "processing DEBUG variable with value '$DEBUG' in $funcname"
esac
fi
else
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) die 255 "processing variable IGNORE_FIXME with value '$IGNORE_FIXME' in $0"
esac
fi
}; alias efixme='efixme "$LINENO"'
# Resolve root
rootCheck() { funcname=rootCheck
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) edebug "Resolving root on user with ID '$(id -u)"
esac
if [ "$(id -u)" = 0 ]; then
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) edebug "Script has been executed as user with ID 0, assuming root"
esac
# NOTICE(Krey): We are prefixing root commands with '$SUDO', this is done to make sure that we are not using sudo here
unset SUDO
funcname="$myName"
return 0
# NOTICE(Krey): The ID 33333 is used by gitpod
elif [ "$(id -u)" = 1000 ] || [ "$(id -u)" = 33333 ]; then
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) ewarn "Script $myName is not expected to run as non-root, trying to elevate root.."
esac
if command -v sudo 1>/dev/null; then
case "$LANG" in
en-*|*) einfo "Found 'sudo' that can be used for root elevation"
esac
SUDO=sudo
funcname="$myName"
return 0
elif command -v su 1>/dev/null; then
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) einfo "Found 'su' that can be used for a root elevation"
esac
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) ewarn "This will require the end-user to parse a root password multiple times assuming that root has a password set"
esac
SUDO=su
funcname="$myName"
return 0
elif ! command -v sudo 1>/dev/null && ! command -v su 1>/dev/null; then
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) die 3 "Script $myName depends on root permission to install packages where commands 'sudo' nor 'su' are available for root elevation"
esac
funcname="$myName"
return 0
else
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) die 225 "processing root on non-root"
esac
fi
else
case "$LANG" in
# FIXME-TRANSLATE: Translate to more languages
en-*|*) die 3 "Unknown user ID '$(id -u)' has been parsed in script $myName"
esac
fi
}
# Identify system
# FIXME: /etc/issue could also be used to identify the system
# FIXME: /etc/debian_version can also be used
# FIXME: /etc/devuan_version can also be used
if command -v "$UNAME" 1>/dev/null; then
unameKernel="$("$UNAME" -s)"
edebug "Identified the kernel as '$unameKernel"
case "$unameKernel" in
Linux)
KERNEL="$unameKernel"
# Assume Linux Distro and release
# NOTICE(Krey): We are expecting this to return a lowercase value
if command -v "$LSB_RELEASE" 1>/dev/null; then
assumedDistro="$("$LSB_RELEASE" -si | "$TR" :[upper]: :[lower]:)"
assumedRelease="$("$LSB_RELEASE" -cs | "$TR" :[upper]: :[lower]:)"
elif ! command -v "$LSB_RELEASE" 1>/dev/null && [ -f /etc/os-release ]; then
assumedDistro="$("$GREP" -o "^ID\=.*" /etc/os-release | "$SED" s/ID=//gm)"
assumedRelease="$("$GREP" -o"^VERSION_CODENAME\=.*" /etc/os-release | "$SED" s/VERSION_CODENAME=//gm)"
elif ! command -v "$LSB_RELEASE" 1>/dev/null && [ ! -f /etc/os-release ]; then
die 1 "Unable to identify linux distribution using command 'lsb_release' nor file '/etc/os-release'"
else
die 255 "attempting to assume linux distro and release"
fi
edebug "Identified distribution as '$assumedDistro'"
edebug "Identified distribution release as '$assumedRelease'"
# Verify Linux Distro
efixme "Add sanitization logic for other linux distributions"
case "$assumedDistro" in
ubuntu | debian | fedora | nixos | opensuse | gentoo | exherbo)
DISTRO="$assumedDistro"
;;
*) die fixme "Unexpected Linux distribution '$assumedDistro' has been detected."
esac
# Verify Linux Distro Release
efixme "Sanitize verification of linux distro release"
assumedRelease="$RELEASE"
;;
FreeBSD | Redox | Darwin | Windows)
KERNEL="$unameKernel"
;;
*) die 255 "Unexpected kernel '$unameKernel'"
esac
elif ! command -v "$UNAME" 1>/dev/null; then
die 1 "Standard command '$UNAME' is not available on this system, unable to identify kernel"
else
die 255 "Identifying system"
fi
# Define hostname
# NOTICE: Variable 'HOSTNAME' is not defined on POSIX sh
if command -v hostname 1>/dev/null; then
HOSTNAME="$(hostname)"
elif [ -s /etc/hostname ]; then
HOSTNAME="$(cat /etc/hostname)"
elif ! command -v hostname 1>/dev/null && [ ! -s /etc/hostname ]; then
die false "Unable to determine the hostname from command 'hostname' (which doesn't exists) and from file /etc/hostname (that doesn't exists or is blank)"
else
die unexpected "processing hostname"
fi
# Argument management
while [ "$#" -gt 0 ]; do case "$1" in
install-deps)
packageManagement
;;
test-docker-debian)
efixme "Implement logic to make sure that docker is available"
die fixme "Implement tests for debian on docker"
$SUDO docker run debian sh -c "true \
&& apt-get update -q \
&& apt-get install -qy lsb-release \
&& sh $0"
;;
--help|help)
efixme "HELP_MESSAGE"
;;
*)
die 2 "FIXME_MESSAGE"
;;
esac; done