605 lines
14 KiB
Bash
Executable File
605 lines
14 KiB
Bash
Executable File
#!/usr/bin/env /bin/bash
|
|
|
|
export BASHTOOLS_DIR="${HOME}/.bashtools"
|
|
if [[ $1 == "install" ]]; then
|
|
mkdir -p "$BASHTOOLS_DIR"
|
|
cp -a $0 "$BASHTOOLS_DIR/"
|
|
echo "Bashtools has been installed in $BASHTOOLS_DIR"
|
|
$(return 2>/dev/null)
|
|
rv="$?"
|
|
if [[ $rv -ne 0 ]]; then
|
|
exit 1
|
|
else
|
|
return
|
|
fi
|
|
fi
|
|
|
|
#true # need a successful command before the sourced script check
|
|
#$(return 2>/dev/null)
|
|
#rv="$?"
|
|
#if [[ $rv -ne 0 ]]; then
|
|
# echo "ERROR: This script should be sourced ('. $0') rather than run directly."
|
|
# return 2>/dev/null # jist in case
|
|
# exit 1;
|
|
#fi
|
|
|
|
|
|
if [[ $1 != "reload" && -n $HAVE_BASHTOOLS ]]; then
|
|
return
|
|
fi
|
|
export HAVE_BASHTOOLS=1
|
|
|
|
SPINDELAY=0.05 # delay in ms between spinner frames
|
|
SPINNERFRAMES='|/-\'
|
|
SPINNERFILE="$BASHTOOLS_DIR/spinner.pid"
|
|
|
|
[[ ! -e $BASHTOOLS_DIR ]] && mkdir "$BASHTOOLS_DIR"
|
|
[[ -e $SPINNERFILE ]] && rm -f "${SPINNERFILE}"
|
|
|
|
|
|
export BOLD="\033[1m"
|
|
export PLAIN="\033[0m"
|
|
export ITALIC="\033[3m"
|
|
export STRIKE="\033[9m"
|
|
export UNDERLINE="\033[4m"
|
|
export RED="\033[31m"
|
|
export YELLOW="\033[33m"
|
|
export GREEN="\033[32m"
|
|
export BLUE="\033[34m"
|
|
export MAGENTA="\033[35m"
|
|
export CYAN="\033[36m"
|
|
export ORANGE="${PLAIN}\033[38;2;255;165;0m"
|
|
export ORANGEBOLD="${BOLD}\033[38;2;255;220;0m"
|
|
|
|
export MAGENTARGB="${PLAIN}\033[38;2;208;65;126m"
|
|
export MAGENTARGBBOLD="${BOLD}\033[38;2;255;135;196m"
|
|
|
|
export INFOCOL="$CYAN"
|
|
export INFORMCOL="$ORANGE"
|
|
export INFORMCOLB="$ORANGEBOLD"
|
|
export NOTIFYCOL="$MAGENTARGB"
|
|
export NOTIFYCOLB="$MAGENTARGBBOLD"
|
|
|
|
export GREY="\033[38;2;110;110;110m"
|
|
export LINK="$BLUE$UNDERLINE"
|
|
|
|
export UNDERLINE_PRINTED=$(echo -en "$UNDERLINE")
|
|
export UNDERLINE_LENGTH=${#UNDERLINE_PRINTED}
|
|
|
|
export REGEXP_IP='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
|
|
|
function noansi() {
|
|
local allcols c
|
|
allcols=$(set | egrep "\\033.*m\'" | sed 's/=.*//')
|
|
for c in $allcols; do
|
|
eval "$c=''"
|
|
done
|
|
}
|
|
|
|
# provide specific "bold" colour
|
|
function cecho() { # [-n] [-s] col boldcol "str"
|
|
local col bcol str minusn="" autobold=0
|
|
|
|
while [[ ${1:0:1} == "-" ]]; do
|
|
if [[ $1 == "-n" ]]; then
|
|
minusn="-n"
|
|
fi
|
|
if [[ $1 == "-s" ]]; then
|
|
autobold=1
|
|
fi
|
|
shift
|
|
done
|
|
col="$1"
|
|
shift
|
|
if [[ $autobold -eq 1 ]]; then
|
|
bcol="$col$BOLD"
|
|
else
|
|
bcol="$1"
|
|
shift
|
|
fi
|
|
str="$*"
|
|
str=${str//\^b/${bcol}}
|
|
str=${str//\^p/${PLAIN}${col}}
|
|
str=${str//\^i/${col}${ITALIC}}
|
|
str=${str//\^u/${col}${UNDERLINE}}
|
|
str=${str//\^s/${col}${STRIKE}}
|
|
|
|
echo $minusn -e "${col}${str}${PLAIN}"
|
|
}
|
|
|
|
# use bold version of supplied colour
|
|
function csecho() { # [-n] col "str"
|
|
local args col
|
|
while [[ $# -gt 2 ]]; do
|
|
args="$args $1"
|
|
shift
|
|
done
|
|
col="$1"
|
|
shift
|
|
cecho $args -s "$col" "$*"
|
|
}
|
|
|
|
alias try=notify
|
|
function notify() {
|
|
#echo -en "${NOTIFYCOLB}* ${NOTIFYCOL}$*...${PLAIN} "
|
|
cecho -n "$NOTIFYCOL" "$NOTIFYCOLB" "^b* ^p$*... " >/dev/stderr
|
|
if [[ $NOSPINNER -ne 1 ]]; then
|
|
start_spinner &
|
|
tput civis >/dev/stderr
|
|
fi
|
|
innotify=1
|
|
}
|
|
|
|
function inform() {
|
|
cecho "$INFORMCOL" "$INFORMCOLB" "^b* ^p$* " >/dev/stderr
|
|
}
|
|
|
|
function info() {
|
|
[[ $AUTOYES -eq 1 ]] && return
|
|
csecho "${INFOCOL}" "^b>>^p $*" >/dev/stderr
|
|
}
|
|
|
|
function ok() {
|
|
local msg=${*:-ok}
|
|
stop_spinner
|
|
[[ $innotify -eq 0 ]] && return 1
|
|
innotify=0
|
|
echo -e "$GREEN$msg$PLAIN" >/dev/stderr
|
|
}
|
|
|
|
function fail() {
|
|
local msg=${*:-failed}
|
|
stop_spinner
|
|
[[ $innotify -eq 0 ]] && return 1
|
|
innotify=0
|
|
echo -e "$RED$msg$PLAIN" >/dev/stderr
|
|
}
|
|
|
|
function partial() {
|
|
local msg=${*:-partial}
|
|
stop_spinner
|
|
[[ $innotify -eq 0 ]] && return 1
|
|
innotify=0
|
|
echo -e "$YELLOW$msg$PLAIN" >/dev/stderr
|
|
}
|
|
|
|
function warn() {
|
|
cecho -s "$YELLOW" "^bWarning: ^p$*" >/dev/stderr
|
|
}
|
|
|
|
function error() {
|
|
cecho -s "$RED" "^bERROR: ^p$*" >/dev/stderr
|
|
}
|
|
|
|
function start_spinner() {
|
|
local idx=0 len spid
|
|
|
|
(
|
|
len=${#SPINNERFRAMES}
|
|
echo -n " " >/dev/stderr
|
|
while [ 1 ] ; do
|
|
echo -en "\b${SPINNERFRAMES:$idx:1}" >/dev/stderr
|
|
idx=$((idx + 1))
|
|
[[ $idx -ge $len ]] && idx=0
|
|
sleep $SPINDELAY
|
|
done
|
|
) &
|
|
spid=$!
|
|
echo $spid >>"$SPINNERFILE"
|
|
}
|
|
|
|
function stop_spinner() {
|
|
local f pid
|
|
|
|
[[ $NOSPINNER -eq 1 ]] && return 0
|
|
|
|
[[ ! -e "$SPINNERFILE" ]] && return 1
|
|
|
|
while read -r pid ; do
|
|
if [[ $pid = *[[:digit:]]* ]]; then
|
|
{ kill $pid && wait $pid; } 2>/dev/null
|
|
echo -en "\b " >/dev/stderr
|
|
fi
|
|
done < "$SPINNERFILE"
|
|
rm -f "$SPINNERFILE"
|
|
tput cnorm >/dev/stderr
|
|
}
|
|
|
|
function getsysstats() {
|
|
CPUS=$(/usr/bin/grep ^processor /proc/cpuinfo | /usr/bin/wc -l | /usr/bin/bc)
|
|
MEM_GB=$(dmidecode -t memory | awk 'BEGIN { tot=0 } /Size: [0-9]/ { tot += $2 } END { print tot }')
|
|
MEM_MB=$(echo "$MEM_GB * 1024" | bc )
|
|
DISKFREE_MB=$(df -m /data | grep -v Filesystem | awk '{ print $4 }')
|
|
DISKFREE_GB=$(echo "$DISKFREE_MB / 1024" | bc )
|
|
}
|
|
|
|
# menu [ -r RETVAR ] [-R NUMRETVAR] [-a] [-c num] [-l v|h ] [-x AB] [ -q "question text" ] [ -d default ] choice1 choice2 ... choiceN [ -D desc1 desc2 ... descN ]
|
|
function menu() {
|
|
local answer answernum autoselect bestval
|
|
local cols choice choiceformat cperrow cwidth default defaultnum desc
|
|
local idx layout mode n nchoices ndescs
|
|
local question retvar retvarnum thislen thisnum thistext
|
|
local x y repfrom repto
|
|
local found
|
|
|
|
autoselect=0
|
|
default=""
|
|
question="Select an option:"
|
|
mode="choices"
|
|
layout=v # 'v'ertical or 'h'orizontal
|
|
nchoices=0
|
|
ndescs=0
|
|
cwidth=0
|
|
retvar=_SELECTION
|
|
cperrow=-1
|
|
found=0
|
|
|
|
while [ $# -ge 1 ]; do
|
|
case $1 in
|
|
"-a")
|
|
autoselect=1
|
|
;;
|
|
"-c")
|
|
shift
|
|
cperrow="$1"
|
|
;;
|
|
"-d")
|
|
shift
|
|
default="$1"
|
|
;;
|
|
"-l")
|
|
shift
|
|
layout="$1"
|
|
;;
|
|
"-r")
|
|
shift
|
|
retvar="$1"
|
|
;;
|
|
"-R")
|
|
shift
|
|
retvarnum="$1"
|
|
;;
|
|
"-x")
|
|
shift
|
|
repfrom="${1:0:1}"
|
|
repto="${1:1:1}"
|
|
;;
|
|
"-q")
|
|
shift
|
|
question="$1"
|
|
;;
|
|
"-D")
|
|
mode="descriptions"
|
|
;;
|
|
*)
|
|
if [ "$mode" == "choices" ]; then
|
|
choice[${nchoices}]="$1"
|
|
if [[ ! -z $repfrom && ! -z $repto ]]; then
|
|
choice[${nchoices}]=`echo ${choice[$nchoices]} | tr "$repfrom" "$repto"`
|
|
fi
|
|
thislen=${#1}
|
|
if [ $thislen -gt $cwidth ]; then
|
|
cwidth=$thislen
|
|
fi
|
|
nchoices=$((nchoices + 1))
|
|
else
|
|
desc[${ndescs}]="$1"
|
|
|
|
# length of this description + length of matching choice
|
|
# + 3 for parantheses
|
|
thislen=$(( ${#1} + ${#choice[$ndescs]} + 3))
|
|
|
|
ndescs=$((ndescs + 1))
|
|
|
|
if [ $thislen -gt $cwidth ]; then
|
|
cwidth=$thislen
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
shift
|
|
done
|
|
|
|
# Validate layout
|
|
if [[ ! $layout =~ v|h ]] ; then
|
|
cecho "$RED" "ERROR in ask(): invalid layout. Must be 'v'ertical or 'h'orizontal. [$question]"
|
|
die
|
|
fi
|
|
|
|
# Validate descriptions
|
|
if [ $ndescs -gt $nchoices ]; then
|
|
cecho "$RED" "ERROR in ask(): Number of descriptions ($ndescs) is larger than number of choices ($nchoices)! [$question]"
|
|
die
|
|
fi
|
|
|
|
# Validate default choice (if given)
|
|
if [ -n "$default" ]; then
|
|
defaultnum=-1
|
|
for n in ${!choice[@]}; do
|
|
if [ "${choice[$n]}" == "$default" ]; then
|
|
defaultnum=$n
|
|
fi
|
|
done
|
|
if [ $defaultnum -eq -1 ]; then
|
|
cecho "$RED" "ERROR in ask(): given default '$default' does not match any choice. [$question]"
|
|
echo " choices are:"
|
|
for n in ${!choice[@]}; do
|
|
echo " ${choice[$n]}"
|
|
done
|
|
die
|
|
fi
|
|
fi
|
|
|
|
# Determine choice width - longest choice + 6 chars
|
|
# to account for 'xxx) '
|
|
cwidth=$((cwidth + 5))
|
|
|
|
# Determine maximum amount of choices per row
|
|
if [ $cperrow -eq -1 ]; then
|
|
if [ $nchoices -le 3 ]; then
|
|
cperrow=1
|
|
else
|
|
cols=$(tput cols)
|
|
cperrow=$((cols / cwidth))
|
|
fi
|
|
fi
|
|
|
|
# Reduce choices per row to minimise (nchoices % cperrow).
|
|
# ie. try to make list of choices as close to a
|
|
# grid shape as possible.
|
|
#
|
|
bestval=$(( cperrow - (nchoices % cperrow) ))
|
|
n=$((cperrow - 1))
|
|
while [ $n -ge 1 ]; do
|
|
thisval=$(( cperrow - (nchoices % n) ))
|
|
if [ $thisval -lt $bestval ]; then
|
|
bestval=$thisval
|
|
cperrow=$n
|
|
fi
|
|
n=$((n - 1))
|
|
done
|
|
|
|
# Determine how many rows we'll need for a
|
|
# vertical layout (ie. counting downwards instead
|
|
# of right).
|
|
if [ "$layout" == "v" ]; then
|
|
choicerows=$((nchoices / cperrow))
|
|
if [ $((nchoices % cperrow)) -gt 0 ]; then
|
|
choicerows=$((choicerows + 1))
|
|
fi
|
|
fi
|
|
|
|
# printf format string
|
|
choiceformat="%3s) %-$((cwidth-5))s"
|
|
|
|
answer=""
|
|
# Prompt for a choice until we get a valid answer.
|
|
while [ -z "$answer" ]; do
|
|
# Display menu
|
|
echo -e "$question"
|
|
|
|
if [ "$layout" == "h" ]; then
|
|
for n in ${!choice[@]}; do
|
|
if [ -n "${desc[$n]}" ]; then
|
|
thistext="${choice[$n]} (${desc[$n]})"
|
|
else
|
|
thistext="${choice[$n]}"
|
|
fi
|
|
|
|
thisnum=$((n + 1))
|
|
if [ "${choice[$n]}" == "${default}" ]; then
|
|
thisnum="*$thisnum"
|
|
fi
|
|
|
|
printf "$choiceformat" "$thisnum" "${thistext}"
|
|
if [ $(( (n + 1) % $cperrow )) -eq 0 ]; then
|
|
printf "\n" # newline
|
|
fi
|
|
done
|
|
else # ie. vertical
|
|
n=0
|
|
for (( y=1; y<=$choicerows; y++)); do
|
|
for (( x=0; x<$cperrow; x++)); do
|
|
idx=$((n + (x * choicerows)))
|
|
if [ $idx -lt $nchoices ]; then
|
|
|
|
if [ -n "${desc[$idx]}" ]; then
|
|
thistext="${choice[$idx]} (${desc[$idx]})"
|
|
else
|
|
thistext="${choice[$idx]}"
|
|
fi
|
|
|
|
thisnum=$((idx + 1))
|
|
if [ "${choice[$idx]}" == "${default}" ]; then
|
|
thisnum="*$thisnum"
|
|
fi
|
|
|
|
printf "$choiceformat" "$thisnum" "${thistext}"
|
|
fi
|
|
done
|
|
n=$((n + 1))
|
|
printf "\n" # newline
|
|
done
|
|
fi
|
|
|
|
printf "\n"
|
|
if [ -n "$default" ]; then
|
|
printf " (* denotes default)\n"
|
|
fi
|
|
printf -- "-> "
|
|
|
|
# If there's only one answer, just select it. Useful for
|
|
# cases where we are basing our list of choices on a dynamic variable.
|
|
if [ $autoselect -eq 1 -a $nchoices -eq 1 ]; then
|
|
answernum=1
|
|
echo "1 (autoselect)" # make it look like we typed it
|
|
else
|
|
read answernum
|
|
fi
|
|
|
|
if [ -z "$answernum" ]; then
|
|
if [ -n "$default" ]; then
|
|
answer=$default
|
|
info "[defaulting to $answer]"
|
|
|
|
# populate 'answernum' correctly
|
|
for z in ${!choice[@]}; do
|
|
if [[ ${choice[$z]} == $answer ]]; then
|
|
answernum=$((z + 1))
|
|
break
|
|
fi
|
|
done
|
|
|
|
else
|
|
printf "Invalid response.\n\n"
|
|
fi
|
|
elif [[ ! $answernum =~ ^[0-9]*$ ]] ; then
|
|
# try to match based on name
|
|
found=0
|
|
shopt -s nocasematch
|
|
for z in ${!choice[@]}; do
|
|
if [[ ${choice[$z]} =~ $answernum ]]; then
|
|
possanswernum[$found]=$((z + 1))
|
|
possanswer[$found]=${choice[z]}
|
|
found=$((found + 1))
|
|
fi
|
|
done
|
|
if [[ $found -eq 1 ]]; then
|
|
selectedposs=${possanswer[0]}
|
|
selectedpossnum=${possanswernum[0]}
|
|
answerhilite=${selectedposs/${answernum}/${BOLD}${answernum}${PLAIN}${CYAN}}
|
|
star=""
|
|
if [[ -z $BOLD ]]; then
|
|
star="*"
|
|
fi
|
|
printf "${CYAN}Matched '${star}${answerhilite}${star}'.${PLAIN}\n\n"
|
|
answernum="$selectedpossnum"
|
|
answer="$selectedposs"
|
|
elif [[ $found -gt 1 ]]; then
|
|
printf "${RED}Invalid response - '${BOLD}$answernum${PLAIN}${RED}' matches $found answers.${PLAIN}\n\n"
|
|
for z in ${!possanswer[@]}; do
|
|
echo -e " ${RED}${possanswernum[$z]}) ${possanswer[$z]}"
|
|
done
|
|
echo -e "${PLAIN}"
|
|
else
|
|
printf ${RED}"Invalid response.${PLAIN}\n\n"
|
|
fi
|
|
shopt -u nocasematch
|
|
elif [ $answernum -lt 1 -o $answernum -gt $nchoices ]; then
|
|
printf "Choice out of range.\n\n"
|
|
else
|
|
answer=${choice[$((answernum - 1))]}
|
|
fi
|
|
done
|
|
|
|
answer="${answer//\'/\\\'}"
|
|
eval "$retvar=$'$answer'"
|
|
eval "$retvarnum='$answernum'"
|
|
return 0
|
|
}
|
|
|
|
function ask() { # [-s == dont echo input] $1 = prompt $default_val $2 = return_variable_name
|
|
local answer prompt default retvar readopts=""
|
|
if [[ $1 == "-s" ]]; then
|
|
readopts="-s"
|
|
shift
|
|
fi
|
|
prompt="$1"
|
|
default="$2"
|
|
retvar="$3"
|
|
cecho -n -s "$YELLOW" "$1 "
|
|
read $readopts answer
|
|
[[ -z $answer ]] && answer="$default"
|
|
eval "$retvar=\"$answer\""
|
|
}
|
|
|
|
function checkreqs() { # appname cpus ram_in_gb disk_in_gb
|
|
local rv=0 appname errs x
|
|
appname="$1"
|
|
shift
|
|
getsysstats
|
|
try "Checking system requirements"
|
|
if [[ $CPUS -lt $1 ]]; then
|
|
errs+=("- CPUs required: ^b${1}^p (system has $CPUS)")
|
|
fi
|
|
if [[ $MEM_GB -lt $2 ]]; then
|
|
errs+=("- RAM required: ^b${2} GB^p (system has ${MEM_GB} GB)")
|
|
fi
|
|
if [[ $DISKFREE_GB -lt $3 ]]; then
|
|
errs+=("- Disk space required: ^b${3} GB^p (system has ${DISKFREE_GB} GB)")
|
|
fi
|
|
if [[ -z $errs ]]; then
|
|
ok
|
|
rv=0
|
|
else
|
|
fail
|
|
error "This system does not meet the requirements for ^b${appname}^p."
|
|
for x in ${!errs[@]}; do
|
|
cecho -s "$RED" " ${errs[$x]}"
|
|
done
|
|
|
|
rv=1
|
|
|
|
fi
|
|
return $rv
|
|
}
|
|
|
|
function generate_ssl_cert() { # usage: $0 fqdn (populates global $sslcert and $sslkey )
|
|
local ssp name
|
|
local country state loc org orgunit email cn
|
|
name="$1"
|
|
|
|
sslcert=""
|
|
sslkey=""
|
|
|
|
[[ -z $name ]] && return 1;
|
|
|
|
sslp=$(cat /proc/sys/kernel/random/uuid)
|
|
|
|
cd /etc/ssl/certs/
|
|
openssl genrsa -aes128 -passout pass:${sslp} 2048 > $name.key
|
|
openssl rsa -in $name.key -passin pass:${sslp} -out $name.key
|
|
|
|
country=AU
|
|
state=Empty
|
|
loc=Empty
|
|
org=NTT
|
|
orgunit=IT
|
|
email=root@localhost
|
|
cn=${fqdn}
|
|
|
|
|
|
openssl req -utf8 -new -key $name.key -out $name.csr -subj "/C=$country/ST=$state/L=$loc/O=$org/OU=orgunit/CN=$cn/emailAddress=$email"
|
|
|
|
# note: change this later to:
|
|
# 1. use our CA
|
|
# 2. not last for 1000 years
|
|
openssl x509 -in $name.csr -out $name.crt -req -signkey $name.key -days 36500
|
|
chmod 600 $name.key
|
|
|
|
# Set globals
|
|
sslcert=/etc/ssl/certs/$name.crt
|
|
sslkey=/etc/ssl/certs/$name.key
|
|
|
|
cd - >/dev/null 2>&1
|
|
|
|
return 0
|
|
}
|
|
|
|
function dnslookup() { # $1 = a_record_to_look_up
|
|
local answer rv ip digargs=""
|
|
if [[ $ip =~ $REGEXP_IP ]]; then
|
|
digargs="-x"
|
|
fi
|
|
answer=$(dig +short $digargs ${1} 2>/dev/null)
|
|
rv=$?
|
|
answer=${answer%.}
|
|
echo "$answer"
|
|
return $rv
|
|
}
|
|
|
|
#declare -fx $(bash -c "source $0 &> /dev/null; compgen -A function")
|
|
declare -fx $(compgen -A function | grep -v ^_)
|
|
|